Ответ 1
Выражение SFINAE объясняется достаточно хорошо в документе, который вы связали, я думаю. Это SFINAE в выражениях. Если выражение внутри decltype
недействительно, хорошо, удалите функцию из VIP-зала перегрузок. Вы можете найти нормативную формулировку в конце этого ответа.
Заметка о VС++: они не реализовали ее полностью. На простых выражениях это может сработать, но на других это не будет. См. Обсуждение в комментариях в этом ответе для примеров, которые терпят неудачу. Чтобы сделать его простым, это не сработает:
#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): 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 Подстановка происходит во всех типах и выражениях, которые используются в типе функции и в объявлениях параметров шаблона. Выражения включают не только постоянные выражения, такие как те, которые появляются в границах массива или как аргументы шаблона nontype , но также общие выражения (т.е. непостоянные выражения) внутри
sizeof
,decltype
и других контекстах, которые допускают не константные выражения.p8 Если подстановка приводит к недопустимому типу или выражению, тип дедукции терпит неудачу. Недопустимый тип или выражение - это те, которые были бы плохо сформированы, если они были написаны с использованием замещенных аргументов. [...]