Что такое "выражение SFINAE"?
At http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx, команда VC++ официально заявляет, что они еще не реализовали основную функцию C++11 "Expression 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"?
1 ответ:
выражение 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); }
С лязгом, это выводит ожидаемое:
может позвонить со ссылкой
Может вызвать с указателем
Не мог позвонитьС MSVC, я получаю... ну, ошибка компилятора:
1>src\main.cpp(20): error C2995: ''unknown-type' test(C,F)' : function template has already been defined 1> src\main.cpp(11) : see declaration of 'test'также кажется, что GCC 4.7.1 не совсем подходит для этой задачи:
source.cpp: In substitution of 'template decltype ((c.*f(), void())) test(C, F) [with C = X*; F = void (X::*)()]': source.cpp:29:17: required from here source.cpp:11:6: error: cannot apply member pointer 'f' to 'c', which is of non-class type 'X*' source.cpp: In substitution of 'template decltype ((c.*f(), void())) test(C, F) [with C = int; F = int]': source.cpp:30:16: required from here source.cpp:11:6: error: 'f' cannot be used as a member pointer, since it is of type '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 если подстановка приводит к недопустимому типу или выражению, вывод типа завершается ошибкой. Недопустимый тип или выражение-это тот, который был бы неверно сформирован, если бы был написан с использованием подставленных аргументов. [...]