Что такое «выражение SFINAE»?

В http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx, Команда VC ++ официально заявляет, что они еще не реализовали базовую функцию C ++ 11 «Выражение SFINAE». Тем не менее, следующие примеры кода скопированы из http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html принимаются компилятором VC ++.

пример 1:

template <int I> struct A {};

char xxx(int);
char xxx(float);

template <class T> A<sizeof(xxx((T)0))> f(T){}

int main()
{
f(1);
}

пример 2:

struct X {};
struct Y
{
Y(X){}
};

template <class T> auto f(T t1, T t2) -> decltype(t1 + t2); // #1
X f(Y, Y);  // #2

X x1, x2;
X x3 = f(x1, x2);  // deduction fails on #1 (cannot add X+X), calls #2

У меня вопрос: что такое «Выражение SFINAE»?

55

Решение

Выражение SFINAE достаточно хорошо объяснено в статье, которую вы связали, я думаю. Это SFINAE по выражениям. Если выражение внутри decltype не действует, ну, выкинуть функцию из VIP зала перегрузок. Вы можете найти нормативную формулировку в конце этого ответа.

Примечание по VC ++: они не реализовали это полностью. На простых выражениях это может сработать, а на других — нет. Смотрите обсуждение в комментариях на этот ответ для примеров, которые терпят неудачу. Для простоты это не сработает:

#include <iostream>

// catch-all case
void test(...)
{
std::cout << "Couldn't call\n";
}

// catch when C is a reference-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c.*f)(), void()) // 'C' is reference type
{
std::cout << "Could call on reference\n";
}

// catch when C is a pointer-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c->*f)(), void()) // 'C' is pointer type
{
std::cout << "Could call on pointer\n";
}

struct X{
void f(){}
};

int main(){
X x;
test(x, &X::f);
test(&x, &X::f);
test(42, 1337);
}

С Clang это выводит ожидаемое:

Могли бы позвонить со ссылкой
Можно позвонить с указателем
Не могу позвонить

С MSVC я получаю … ну, ошибка компилятора:

1> src \ main.cpp (20): ошибка C2995: тест «неизвестного типа» (C, F) ': шаблон функции уже определен
1> src \ main.cpp (11): см. Объявление 'test'

Также кажется, что GCC 4.7.1 не совсем подходит для этой задачи:

source.cpp: при замене 'шаблона decltype ((c. * f (), void ())) test (C, F) [с C = X *; F = void (X :: *) ()] ':
source.cpp: 29: 17: требуется отсюда
source.cpp: 11: 6: ошибка: невозможно применить указатель члена 'f' к 'c', который имеет тип не-класса 'X *'
source.cpp: в подстановке 'template decltype ((c. * f (), void ())) test (C, F) [с C = int; F = int] ':
source.cpp: 30: 16: требуется отсюда
source.cpp: 11: 6: ошибка: «f» не может использоваться как указатель на член, так как он имеет тип «int»

Широко используется выражение SFINAE при определении черт, например черты, позволяющей проверить, выполняет ли класс определенную функцию-член:

struct has_member_begin_test{
template<class U>
static auto test(U* p) -> decltype(p->begin(), std::true_type());
template<class>
static auto test(...) -> std::false_type;
};

template<class T>
struct has_member_begin
: decltype(has_member_begin_test::test<T>(0)) {};

Живой пример. (Что удивительно, снова работает на GCC 4.7.1.)

Смотрите также этот мой ответ, который использует ту же технику в другой среде (иначе без черт).


Нормативная формулировка:

§14.8.2 [temp.deduct]

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

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

p8 Если подстановка приводит к неверному типу или выражению, вывод типа завершается неудачей. Недопустимый тип или выражение — это то, что было бы неправильно сформировано, если было бы написано с использованием замещенных аргументов. […]

68

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]