¿Qué es “Expresión SFINAE”?

En http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx , el equipo de VC ++ declara oficialmente que aún no han implementado la función principal de C ++ 11, “Expresión SFINAE”. Sin embargo, los siguientes ejemplos de código copiados de http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html son aceptados por el comstackdor de VC ++.

Ejemplo 1:

template  struct A {}; char xxx(int); char xxx(float); template  A f(T){} int main() { f(1); } 

ejemplo 2:

 struct X {}; struct Y { Y(X){} }; template  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 

Mi pregunta es: ¿Qué es “Expresión SFINAE”?

Expresión SFINAE se explica bastante bien en el documento que vinculó, creo. Es SFINAE en expresiones. Si la expresión dentro de decltype no es válida, bueno, patea la función desde la sala VIP de sobrecargas. Puede encontrar la redacción normativa al final de esta respuesta.

Una nota sobre VC ++: No lo implementaron por completo . En expresiones simples, podría funcionar, pero en otros, no lo hará. Vea una discusión en los comentarios sobre esta respuesta para ejemplos que fallan. Para hacerlo simple, esto no funcionará:

 #include  // 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 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 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); } 

Con Clang, esto produce lo esperado:

Podría llamar con referencia
Podría llamar con un puntero
No se pudo llamar

Con MSVC, obtengo ... bueno, un error de comstackción:

 1> src \ main.cpp (20): error C2995: prueba '' de tipo desconocido '(C, F)': la plantilla de función ya se ha definido
 1> src \ main.cpp (11): ver statement de 'prueba'

También parece que GCC 4.7.1 no está a la altura de la tarea:

 source.cpp: En sustitución de 'template decltype ((c. * f (), void ())) test (C, F) [con C = X *;  F = vacío (X :: *) ()] ':
 source.cpp: 29: 17: requerido desde aquí
 source.cpp: 11: 6: error: no se puede aplicar el puntero de miembro 'f' a 'c', que es de tipo no de clase 'X *'
 source.cpp: En sustitución de 'template decltype ((c. * f (), void ())) test (C, F) [con C = int;  F = int] ':
 source.cpp: 30: 16: requerido desde aquí
 source.cpp: 11: 6: error: 'f' no se puede utilizar como un puntero de miembro, ya que es de tipo 'int'

Un uso común de Expression SFINAE es cuando se definen rasgos, como un rasgo para verificar si una clase tiene cierta función de miembro:

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

Ejemplo en vivo (Lo cual, sorprendentemente, vuelve a funcionar en GCC 4.7.1.)

Ver también esta respuesta mía , que usa la misma técnica en otro entorno (también conocido como sin rasgos).


Redacción normativa:

§14.8.2 [temp.deduct]

p6 En ciertos puntos del proceso de deducción del argumento de la plantilla, es necesario tomar un tipo de función que haga uso de los parámetros de la plantilla y reemplazar esos parámetros de la plantilla con los argumentos de la plantilla correspondiente. Esto se hace al principio de la deducción del argumento de plantilla cuando cualquier argumento de plantilla explícitamente especificado se sustituye en el tipo de función, y de nuevo al final de la deducción del argumento de plantilla cuando se sustituyen los argumentos de plantilla que se dedujeron u obtuvieron de los argumentos predeterminados .

p7 La sustitución ocurre en todos los tipos y expresiones que se utilizan en el tipo de función y en las declaraciones de parámetros de plantilla. Las expresiones incluyen no solo expresiones constantes como las que aparecen en los límites de la matriz o como argumentos de plantilla sin tipo, sino también expresiones generales (es decir, expresiones no constantes) dentro de sizeof , decltype y otros contextos que permiten expresiones no constantes.

p8 Si una sustitución da como resultado un tipo o expresión no válida, el tipo de deducción falla. Un tipo o expresión inválida es aquella que estaría mal formada si se escribiera utilizando los argumentos sustituidos. [...]