2012-09-29 18 views
50

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 central 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++.¿Qué es "Expresión SFINAE"?

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"?

+0

¿Por qué no se ven en lo obvio: http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error – SChepurin

+5

@SChepurin: Eso explica SFINAE normales , no Expresión SFINAE. Sin embargo, es un buen punto. ¿Sabe OP sobre SFINAE en general? – Xeo

+1

@ Xeo, sí, conozco SFINAE en general. Muchas gracias a ti por tu explicación ilustrativa. – xmllmx

Respuesta

62

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, bien, ejecute la función desde la sala VIP de sobrecargas. Puede encontrar la redacción normativa al final de esta respuesta.

Una nota en VC++: No lo implementaron completamente. En expresiones simples, podría funcionar, pero en otros, no lo hará. Vea una discusión en los comentarios on this answer para ejemplos que fallan. Para hacerlo más 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 Sonido metálico, este emite la esperaba:

Could call with reference
Could call with pointer
Couldn't call

Con MSVC, consigo ... bueno, un error del compilador:

 
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 es del todo a la altura:

 
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 Expresión SFINAE es la hora de definir los rasgos, como un rasgo para comprobar si una clase de deporte una cierta función miembro: (. Lo cual, sorprendentemente, funciona de nuevo en GCC 4.7.1)

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)) {}; 

Live example.

Vea también this answer of mine, que usa la misma técnica en otro entorno (también conocido como sin rasgos).


texto normativo:

§14.8.2 [temp.deduct]

p6 At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.

p7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) insidesizeof , decltype, and other contexts that allow non-constant expressions.

p8 If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [...]

+0

Me preguntaba por qué de repente aparecieron enlaces a liveworkspace en lugar de ideone, cuando noté que liveworkspace usa gcc 4.7.2. Ah! ¡Finalmente! –

+2

El enlace de ejemplo en vivo está roto :( –

Cuestiones relacionadas