В 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»?
Выражение 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 Если подстановка приводит к неверному типу или выражению, вывод типа завершается неудачей. Недопустимый тип или выражение — это то, что было бы неправильно сформировано, если было бы написано с использованием замещенных аргументов. […]
Других решений пока нет …