2009-08-08 7 views
10

Stavo leggendo l'articolo di Wikipedia su SFINAE e incontrato esempio di codice seguente:Perché a volte hai bisogno di scrivere `typename T` invece di solo` T`?

struct Test 
{ 
    typedef int Type; 
}; 

template < typename T > 
void f(typename T::Type) {} // definition #1 

template < typename T > 
void f(T) {}    // definition #2 

void foo() 
{ 
    f<Test> (10); //call #1 

    f<int> (10); //call #2 without error thanks to SFINAE 
} 

Ora realtà ho scritto il codice come questo prima, e in qualche modo intuitivamente sapevo che avevo bisogno di digitare "typename T" invece di solo "T". Tuttavia, sarebbe bello conoscere la logica attuale dietro di esso. Qualcuno ha voglia di spiegare?

+1

Vi consiglio di leggere il modello faq: http://womble.decadentplace.org.uk/c++/template-faq.html –

risposta

8

In generale, la sintassi del C++ (ereditata da C) ha un difetto tecnico: il parser DEVE sapere se qualcosa nomina un tipo o no, altrimenti non può risolvere certe ambiguità (ad esempio, è X * Y una moltiplicazione, o la dichiarazione di un puntatore Y agli oggetti di tipo X - tutto dipende dal fatto che X nomi un tipo ...! -). L'aggettivo "typename" ti consente di renderlo perfettamente chiaro ed esplicito quando necessario (il che, come cita un'altra risposta, è tipico quando sono coinvolti i parametri del template ;-).

+1

Intendi "tipicamente quando i parametri del template sono coinvolti" (nel qual caso sono abbastanza sicuro che sia * only * quando sono coinvolti i parametri del template), o che sia un refuso per "che è tipico quando sono coinvolti i parametri del template" ? –

+1

@onebyone, buona cattura, in effetti intendevo "tipico" e non "tipicamente", modificato la mia risposta per risolvere, tx. –

13

La versione breve che è necessario fare typename X::Y ogni volta che X è o dipende da un parametro di modello. Fino a quando X è noto, il compilatore non può dire se Y è un tipo o un valore. Quindi devi aggiungere typename per specificare che si tratta di un tipo.

Ad esempio:

template <typename T> 
struct Foo { 
    typename T::some_type x; // T is a template parameter. `some_type` may or may not exist depending on what type T is. 
}; 

template <typename T> 
struct Foo { 
    typename some_template<T>::some_type x; // `some_template` may or may not have a `some_type` member, depending on which specialization is used when it is instantiated for type `T` 
}; 

Come sbi sottolinea nei commenti, la causa dell'ambiguità è che Y potrebbe essere un membro statico, un enum o una funzione. Senza conoscere il tipo di X, non possiamo dirlo. Lo standard specifica che il compilatore dovrebbe assumere che è un valore a meno che non sia etichettato esplicitamente un tipo utilizzando la parola chiave typename.

E sembra che i commentatori vogliono davvero di citare un altro caso relativo così:;)

Se il nome dipendente è un modello di membro di funzione, e si chiama con un argomento modello esplicito (foo.bar<int>(), per esempio), è necessario aggiungere la parola chiave template prima del nome della funzione, come in foo.template bar<int>().

Il motivo per questo è che senza la parola chiave modello, il compilatore presuppone che bar sia un valore e che si desideri richiamare l'operatore minore di (operator<).

+0

jalf, potrebbe valere la pena menzionare un significato alternativo di 'T :: something' (dati statici, nome della funzione). E una volta che l'hai fatto così bene, potrebbe valere la pena di aggiungere la spiegazione del perché a volte dobbiamo anche iniettare un 'template'. Ha la stessa ragione, quindi non ci vorrà molto di più. – sbi

+0

'template' non è lo stesso ragionamento. Non ha nulla a che fare con i nomi dipendenti. Cade nella categoria dei "problemi che tendono a farti inciampare con i modelli", ma non penso che sia la stessa ragione con "typename". – jalf

+1

@jalf: non ha a che fare con i nomi dei modelli? In ogni caso, le ragioni sono piuttosto analoghe. È consentito solo nel codice del modello e deve essere usato per distinguere i nomi dei membri dipendenti che sono nomi di modelli da nomi di membri dipendenti che non lo sono. –

3

In pratica, è necessaria la parola chiave typename quando si scrive il codice del modello (ovvero si è in un modello di funzione o modello di classe) e si fa riferimento a un indentificatore che dipende da un parametro del modello che potrebbe non essere noto come digita, ma deve essere interpretato come un tipo nel codice del modello.

Nell'esempio, si utilizza typename T::Type alla definizione 1 perché T::Type dipende dal parametro modello T e potrebbe altrimenti essere un membro dati.

Non è necessario typename T alla definizione n. 2 come T è dichiarato essere un tipo come parte della definizione del modello.

Problemi correlati