Что такое "выражение 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 55

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