2010-08-08 22 views
5

Estoy tratando de usar SFINAE para distinguir una clase que tiene un miembro llamado 'nombre'. Configuré las cosas en lo que parece ser el patrón estándar, pero no funciona: en lugar de ignorar silenciosamente la sustitución "fallida", el compilador produce un error.SFINAE: algunas fallas más iguales que otras?

Estoy seguro de que he tropezado con alguna regla de sustitución de plantilla, le agradecería que alguien pudiera explicar cuál.

Este es un ejemplo simplificado. Estoy usando gcc:

template <typename U> string test(char(*)[sizeof(U::name)] = 0) { return "has name!"; } 
template <typename U> string test(...) { return "no name"; } 

struct HasName { string name; } 
struct NoName {} 

cout << "HasName: " << test<HasName>(0) << endl; //fine 
cout << "NoName: " << test<NoName>(0) << endl; //compiler errors: 

//error: size of array has non-integral type `<type error>' 
//error: `name' is not a member of `NoName' 
+0

¿Qué compilador estás usando? g ++ 4.4.3 acepta este código y no emite diagnósticos incluso con '-Wall -Wextra -pedantic'. –

+3

@Tyler: ¿no emite diagnósticos para los puntos y comas que faltan después de las definiciones 'struct'? ;-) –

+0

oops :-); Estoy seguro de que sí (disculpe el tipeo descuidado) –

Respuesta

1

aparece lo siguiente válidos (aunque, como dice Michael, no da necesariamente el resultado que desea en otros compiladores):

#include <string> 
#include <iostream> 
using namespace std; 

template <typename U> string test(char(*)[sizeof(U::name)] = 0) { return "has name!"; } 
template <typename U> string test(...) { return "no name"; } 

struct HasName { static string name; }; 
struct NoName { }; 

int main() { 
    cout << "HasName: " << test<HasName>(0) << endl; 
    cout << "NoName: " << test<NoName>(0) << endl; 
} 

Salida:

HasName: has name! 
NoName: no name 

gcc (GCC) 4.3.4 20090804 (liberación) 1

Comeau también acepta el código .

+0

Algunos puntos de datos adicionales: MSVC 9/10 también acepta el código (incluso sin el 'static' en el miembro' name'), pero no produce el resultado esperado. MinGW (GCC 3.4.5) no compila ni con o sin el miembro como 'estático'. –

+0

@Michael: entonces quizás debería decir "parece estar bien". Normalmente tomo "Comeau lo acepta" como sinónimo de "es C++ válido", pero reconozco que esto es simplemente una muy buena aproximación :-) –

+0

Ah, y mientras Comeau acepta el código, produce el mismo resultado que MSVC (es decir , dice "sin nombre" para ambas llamadas a 'test <>()'). –

0

He aquí un intento de esto:

// Tested on Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01 
template<typename T> 
class TypeHasName 
{ 
private: 
    typedef char (&YesType)[2]; 
    typedef char (&NoType)[1]; 

    struct Base { int name; }; 
    struct Derived : T, Base { Derived(); }; 

    template<typename U, U> struct Dummy; 

    template<typename U> 
    static YesType Test(...); 

    template<typename U> 
    static NoType Test(Dummy<int Base::*, &U::name>*); 

public: 
    enum { Value = (sizeof(Test<Derived>(0)) == sizeof(YesType)) }; 
}; 

#include <string> 
#include <iostream> 

struct HasName { std::string name; }; 
struct NoName {}; 

int main() 
{ 
    std::cout << "HasName: " << TypeHasName<HasName>::Value << std::endl; 
    std::cout << "NoName: " << TypeHasName<NoName>::Value << std::endl; 
    return 0; 
} 

La idea es que si T tiene una variable llamada name, a continuación, Derived tendrá dos variables (name uno de T y uno de Base). Si T no declara una variable name, entonces Derived solo tendrá una de Base.

Si Derived tiene dos name variables, entonces la expresión &U::name en la segunda sobrecarga Test() será ambigua y SFINAE debe eliminar esa función a partir del conjunto de sobrecarga.

Cuestiones relacionadas