c++ - ¿Qué es "Expresión SFINAE"?
templates visual-c++ (1)
Expresión SFINAE se explica bastante bien en el documento que vinculó, creo. Es en.wikipedia.org/wiki/Substitution_failure_is_not_an_error 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 <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);
}
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 compilación:
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''
También parece que GCC 4.7.1 no está a la altura de la tarea:
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''
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<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)) {};
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. [...]
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 compilador de VC ++.
Ejemplo 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);
}
ejemplo 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
Mi pregunta es: ¿Qué es "Expresión SFINAE"?