2009-10-21 14 views
79

In alcuni casi ho visto alcuni messaggi di errore davvero indecifrabili sputati da gcc quando si utilizzano i modelli ... In particolare, ho riscontrato problemi in cui dichiarazioni apparentemente corrette stavano causando errori di compilazione molto strani che magicamente scomparivano anteponendo il "typename" parola chiave all'inizio della dichiarazione ... (Ad esempio, solo la scorsa settimana, stavo dichiarando due iteratori come membri di un'altra classe di modelli e dovevo farlo) ...Ufficialmente, per che cosa è typename?

Qual è la storia su typename?

+3

http://stackoverflow.com/questions/1600464/ – sbi

risposta

124

seguito è la citazione di Josuttis libro :

la typename parola chiave è stata introdotta per specificare che l'identificatore che segue è un tipo. Si consideri il seguente esempio :

template <class T> 
Class MyClass 
{ 
    typename T::SubType * ptr; 
    ... 
}; 

Qui, typename viene utilizzato per chiarire che sottotipo è un tipo di classe di T. Così, ptr è un puntatore al tipo T :: sottotipo. Senza nome tipo, Sottotipo sarebbe considerato un membro statico. Così

T::SubType * ptr 

sarebbe una moltiplicazione del valore SubType di tipo T con ptr.

+1

Ottimo libro. Leggilo una volta e poi mantienilo come riferimento se vuoi. –

+0

bella risposta !!! – Gob00st

20

Stan Lippman's BLog post suggerisce: -

Stroustrup riutilizzato classe parola chiave esistente per specificare un parametro di tipo piuttosto che introdurre una nuova parola chiave che potrebbero ovviamente rompere programmi esistenti. Non è che una nuova parola chiave non è stata considerata - solo che è stato considerato necessario data la sua interruzione potenziale. E fino allo standard ISO-C++ , questo era l'unico modo per dichiarare un parametro di tipo .

Quindi, in pratica Stroustrup riutilizzati classe parola chiave senza introdurre una nuova parola chiave che è cambiato in seguito nello standard per le seguenti ragioni

come l'esempio dato

template <class T> 
class Demonstration { 
public: 
void method() { 
    T::A *aObj; // oops … 
    // … 
}; 

lingua grammatica interpreta T::A *aObj; come espressione aritmetica quindi viene introdotta una nuova parola chiave denominata typename

typename T::A* a6; 

indica al compilatore di trattare la dichiarazione successiva come una dichiarazione.

Dal momento che la parola chiave era sul libro paga, diamine, perché non risolve la confusione causata dalla decisione iniziale di riutilizzare la parola chiave di classe.

Ecco perché abbiamo sia

Si può avere uno sguardo this post, sarà sicuramente aiutare, ho appena estratti da tanto quanto ho potuto

+0

Sì, ma allora perché era una nuova parola chiave 'typename' necessaria, se si potesse utilizzare la parola chiave esistente' class' per lo stesso scopo? – Jesper

+4

@Jesper: Penso che la risposta di Xeno sia confusa qui. 'typename' è diventato necessario per risolvere il problema di analisi come descritto nella risposta di Naveen citando Josuttis. (Non credo che l'inserimento di un 'class' in questo posto avrebbe funzionato.) Solo dopo che la nuova parola chiave è stata accettata per questo caso, è stato anche consentito nelle dichiarazioni degli argomenti del modello (o le definizioni? _), Perché quello' la classe è sempre stata in qualche modo fuorviante. – sbi

11

Si consideri il codice

template<class T> somefunction(T * arg) 
{ 
    T::sometype x; // broken 
    . 
    . 

Purtroppo, il compilatore non è richiesto di essere psichica, e non sa se T :: sometype finirà riferimento a un nome di tipo o un membro statico T. Quindi, si usa typename a raccontarla:

template<class T> somefunction(T * arg) 
{ 
    typename T::sometype x; // works! 
    . 
    . 
4

Due usi:

  1. come parola chiave modello di argomento (invece di 'classe')
  2. Una parola chiave typename dice al compilatore che un identificatore è un tipo (piuttosto che una variabile membro statica)
template <typename T> class X // [1] 
{ 
    typename T::Y _member; // [2] 
} 
3

Il il segreto sta nel fatto che un modello può essere specializzato per alcuni tipi. Ciò significa che può anche definire l'interfaccia completamente diversa per diversi tipi. Ad esempio è possibile scrivere:

template<typename T> 
struct test { 
    typedef T* ptr; 
}; 

template<>   // complete specialization 
struct test<int> { // for the case T is int 
    T* ptr; 
}; 

Si potrebbe chiedere perché è utile e in effetti: sembra davvero inutile. Ma tieni presente che ad esempio std::vector<bool> il tipo reference sembra completamente diverso rispetto agli altri T s. Certo, non cambia il tipo di reference da un tipo a qualcosa di diverso ma ciò nonostante potrebbe accadere.

Ora cosa succede se si scrivono i propri modelli utilizzando questo modello test. Qualcosa di simile

template<typename T> 
void print(T& x) { 
    test<T>::ptr p = &x; 
    std::cout << *p << std::endl; 
} 

sembra essere ok per voi, perché si aspetta che test<T>::ptr è un tipo. Ma il compilatore non lo sa e, di fatto, è addirittura consigliato dallo standard di aspettarsi il contrario, test<T>::ptr non è un tipo. Per dire al compilatore cosa ti aspetti devi prima aggiungere un typename. Il modello corretto appare come questa linea di fondo

template<typename T> 
void print(T& x) { 
    typename test<T>::ptr p = &x; 
    std::cout << *p << std::endl; 
} 

: È necessario aggiungere typename prima di ogni volta che si utilizza un tipo nidificato di un modello nei vostri modelli. (Naturalmente solo se per quel modello interno è utilizzato un parametro template del

5

In alcune situazioni in cui si fa riferimento a un membro del cosiddetto tipo dipendente (che significa "dipendente dal parametro template"), il il compilatore non può sempre dedurre inequivocabilmente il significato semantico del costrutto risultante, perché non sa che tipo di nome è (cioè se si tratta di un nome di un tipo, un nome di un membro di dati o un nome di qualcos'altro). In casi come questi è necessario disambiguare la situazione dicendo esplicitamente al compilatore che il nome appartiene a un typename definito come membro di quel tipo dipendente.

Ad esempio

template <class T> struct S { 
    typename T::type i; 
}; 

In questo esempio la parola typename nella necessaria per il codice per compilare.

La stessa cosa accade quando si desidera fare riferimento a un membro del modello di tipo dipendente, ad esempio a un nome che designa un modello. Hai anche per aiutare il compilatore utilizzando la parola chiave template, anche se è collocato in modo diverso

template <class T> struct S { 
    T::template ptr<int> p; 
}; 

In alcuni casi potrebbe essere necessario utilizzare entrambi

template <class T> struct S { 
    typename T::template ptr<int>::type i; 
}; 

(se ho capito correttamente la sintassi) .

Ovviamente, un altro ruolo della parola chiave typename deve essere utilizzato nelle dichiarazioni dei parametri del modello.

+0

Vedere anche [Una descrizione della parola chiave typename C++] (http://pages.cs.wisc.edu/~driscoll/typename.html) per ulteriori informazioni (in background). – Atafar

1
#include <iostream> 

class A { 
public: 
    typedef int my_t; 
}; 

template <class T> 
class B { 
public: 
    // T::my_t *ptr; // It will produce compilation error 
    typename T::my_t *ptr; // It will output 5 
}; 

int main() { 
    B<A> b; 
    int my_int = 5; 
    b.ptr = &my_int; 
    std::cout << *b.ptr; 
    std::cin.ignore(); 
    return 0; 
}