Дружелюбные чеки SFINAE - PullRequest
       60

Дружелюбные чеки SFINAE

0 голосов
/ 11 октября 2018

Я столкнулся с некоторыми проблемами при попытке сделать объявление друга с помощью проверок sfinae (вы можете просто перейти к примеру кода, если вам не нужны объяснения «почему» и «как»).

По сути, у меня есть некоторый шаблонный класс, объявляющий две закрытые функции-члена.В зависимости от реализации типа шаблона я хочу использовать одну или другую функцию.

Так что, если я не хочу, чтобы компиляция не удалась, закрытая функция, которую я не могу использовать, не может быть создана.Итак, я должен вызвать его через проверку sfinae (независимая функция).Учитывая, что это конфиденциально, я должен заставить свою sfinae проверить друга моего класса.

Однако я не могу этого сделать, как показывает следующий (минимальный) код.Вещи, которые я не хочу менять: прототип класса A (f1 и f2 должны оставаться закрытыми), прототипы классов B1 и B2.

Я понимаю, почему вещи в комментариях терпят неудачу (или я думаю,Да), но я не знаю, как это исправить.

#include <iostream>

template<class T> class A;

template<class T>
auto sfinae_check(T& t, A<T>& a, int)  -> decltype(t.b1(), void());

template<class T>
auto sfinae_check(T& t, A<T>& a, long)  -> decltype(t.b2(), void());

template<class T>
class A
{
    void f1() { t.b1(); }
    void f2() { t.b2(); }

    T& t;

    //friend auto sfinae_check<>(T &t, A<T> &a, int);//obviously mismatches everything
    //friend auto sfinae_check<>(T &t, A<T> &a, int) -> decltype(t.b1(), void()); //failure : no member named b1
    //friend auto sfinae_check<>(T &t, A<T> &a, long) -> decltype(t.b2(), void()); //failure : no member named b2

    public:
        A(T& t) : t(t) {}
        void f() { sfinae_check(t, *this, 0); }
};

template<class T>
auto sfinae_check(T& t, A<T>& a, int)  -> decltype(t.b1(), void())
{
    a.f1();
}

template<class T>
auto sfinae_check(T& t, A<T>& a, long)  -> decltype(t.b2(), void())
{
    a.f2();
}

struct B1
{
    void b1() { std::cout << "b1" << std::endl; }
};

struct B2
{
    void b2() { std::cout << "b2" << std::endl; }
};

int main()
{
    B1 b1; B2 b2;

    A<B1> a1(b1);
    a1.f(); //should print b1

    A<B2> a2(b2);
    a2.f(); //should print b2
}

Ответы [ 2 ]

0 голосов
/ 11 октября 2018

Вся эта схема может быть значительно упрощена (и в соединении тоже), если вы отказываетесь от разных имен f1 и f2 и вместо этого делаете отправку тегов:

template<int> struct tag{};

template<int i> struct priority : priority<i - 1> {};
template<> struct priority <0>{};

template<class T>
auto sfinae_check(T& t, priority<1>)  -> decltype(t.b1(), tag<1>{}) { return {}; }

template<class T>
auto sfinae_check(T& t, priority<0>)  -> decltype(t.b2(), tag<0>{}) { return {}; }

template<class T>
class A
{
    void f(tag<1>) { t.b1(); }
    void f(tag<0>) { t.b2(); }

    T& t;

    public:
        A(T& t) : t(t) {}
        void f() { f(sfinae_check(t, priority<1>{})); }
};

Нет дружбы, нет почтициклические зависимости, и вы увидите точный результат, который вы хотите.И как обледенение сверху, добавление поддержки для другой перегрузки должно быть довольно легким, если возникнет такая необходимость.

Приоритет перегрузок также закодирован здесь (спасибо Jarod42 за напоминание).Поскольку эти теги находятся в цепочке наследования, второй аргумент priority<1>{} может быть предоставлен для любых перегрузок, но в случае жизнеспособности обоих, это будет способствовать более близкому совпадению.

0 голосов
/ 11 октября 2018

Общее простое решение для работы с шаблонными функциями-друзьями в шаблонных классах состоит в том, чтобы просто объявить сам шаблон как друга, а не просто перегрузку, характерную для текущего экземпляра:

template<class T>
class A
{
    ...

    template<typename U>
    friend auto sfinae_check(U &u, A<U> &a, int) -> decltype(u.b1(), void()); 

    template<typename U>
    friend auto sfinae_check(U &u, A<U> &a, long) -> decltype(u.b2(), void()); 

    ...
};

Что, похоже, решает вашу проблемувыпуск: https://gcc.godbolt.org/z/8UGGZM

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...