2010-08-08 13 views
5

Sto tentando di utilizzare SFINAE per distinguere una classe che ha un membro chiamato 'nome'. Metto le cose in quello che sembra essere il modello standard ma non funziona - invece di ignorare silenziosamente la sostituzione 'fallita', il compilatore produce un errore.SFINAE: alcuni fallimenti più uguali degli altri?

Sono sicuro di essermi imbattuto in qualche regola di sostituzione del modello, sarei grato se qualcuno potesse spiegare quale.

Questo è un esempio ridotto. Sto utilizzando 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

Quale compilatore stai utilizzando? g ++ 4.4.3 accetta questo codice e non emette alcuna diagnostica anche con '-Wall -Wextra -pedantic'. –

+3

@Tyler: non emette nemmeno la diagnostica per i punti e virgola mancanti dopo le definizioni di 'struct'? ;-) –

+0

oops :-); Sono sicuro che lo faccia (scusate la digitazione incurante) –

risposta

1

appare quanto segue valida (anche se, come dice Michael, non necessariamente dare il risultato che si desidera su altri compilatori):

#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; 
} 

uscita:

HasName: has name! 
NoName: no name 

gcc (GCC) 4.3.4 20.090.804 (release) 1

Comeau accetta anche il codice .

+0

Alcuni punti dati aggiuntivi: MSVC 9/10 accetta anche il codice (anche senza 'static' sul membro' name'), ma non produce l'output atteso. MinGW (GCC 3.4.5) non lo compila con o senza il membro 'static'. –

+0

@ Michael: quindi forse dovrei dire "sembra buono". Di solito prendo "Comeau accetta" come sinonimo di "è C++ valido", ma riconosco che è solo un'approssimazione molto buona :-) –

+0

Oh, e mentre Comeau accetta il codice, produce lo stesso risultato di MSVC (cioè , dice "nessun nome" per entrambe le chiamate a 'test <>()'). –

0

Ecco un tentativo di questo:

// 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; 
} 

L'idea è che se T ha una variabile denominata name, quindi Derived avrà due name variabili (uno da T e uno dal Base). Se T non dichiara una variabile name, quindi Derived ne avrà uno solo da Base.

Se Derived ha due name variabili, allora l'espressione &U::name nel secondo Test() sovraccarico sarà ambiguo e SFINAE dovrebbe rimuovere tale funzione dal set di sovraccarico.

Problemi correlati