Uso della parola chiave typename con typedef e new

Considera questo codice,

template struct Sample { typename T::X *x; //declare pointer to T's X }; 

Nel codice precedente, la parola chiave typename è richiesta dal compilatore, in modo che possa distinguere tra i tipi nidificati ei valori nidificati nei modelli. Ciò significa che, in assenza della parola chiave typename , il compilatore interpreterà questo come moltiplicazione di T :: X con x,

 T::X *x; //multiply T::X with x 

Quindi in tali situazioni in cui può sorgere ambiguità, la parola chiave typename diventa necessaria per rimuovere ambiguità. Ma ci sono poche situazioni in cui il contesto stesso rimuove le ambiguità. L’ altro argomento tratta i contesti dei parametri di base e di funzione (quest’ultimo però non rimuove l’ambiguità). In questo argomento, desidero in particolare discutere altri due contesti che sembrano non ambigui , ma dobbiamo ancora scrivere typename ,

 typedef typename T::X xtype; pX = new typename T::X; 

In queste due situazioni, le parole chiave typedef e new rendono abbastanza chiaro al compilatore che tutto ciò che segue è type , non value .

Quindi la mia domanda è: perché i compilatori hanno ancora bisogno della parola chiave typename , anche in situazioni non ambigue, come quando usiamo typedef e new ?


    EDIT (dopo aver letto questa risposta di Johannes Schaub ):

     //typedef NOT followed by a type! int typedef A; 

    Questa syntax mi chiede di modificare un po ‘la mia domanda, in modo che il punto che sto cercando di fare possa essere visto dagli altri.

    Considera questo,

     T::X typedef *x; 

    Quindi dal contesto, è ancora abbastanza chiaro al compilatore che T :: X è un tipo, non importa se appare prima di typedef , o dopo typedef . A meno che il C ++ ci permetta di scrivere typedef 5 five o typedef T::value t_value (dove T :: value è value ), la presenza di typedef rimuove tutte le ambiguità e quindi, il nome typename sembra essere un requisito non necessario dallo Standard (in tale situazioni) . Lo stesso argomento vale anche per il new .


    Inoltre, ho scritto un modello di class che sta usando questa struttura come argomento modello:

     struct A { struct X { string name; }; static const int X = 100; }; 

    In particolare voglio sapere se il seguente codice (dal costruttore) è corretto (portatile) o meno,

     //two interesting statements pX = new typename T::X; //T::X means struct X product = T::X * p; //but here, T::X means int X 

    Il codice completo è qui a ideone. Si prega di dare un’occhiata a prima di rispondere. 🙂

    La syntax C ++ è più pazza di così.

     // typedef NOT followed by a type! int typedef A; // new NOT followed by a type! new (0) int; 

    Altri hanno commentato il tuo esempio. Lo specificatore typename non cede alla ricerca ignorando i nomi non di tipo. Quindi se dici un new typename T::X , e c’è un nome di object X in T , sarà comunque trovato al posto del nome del tipo X (GCC tuttavia ignora i nomi non di tipo nella ricerca di un nome dopo un nome di tipo. non è conforms agli standard).


    Risposte alle modifiche:

    Considera questo,

     T::X typedef *x; 

    Quindi dal contesto, è ancora abbastanza chiaro al compilatore che T :: X è un tipo, non importa se appare prima di typedef, o dopo typedef.

    Il compilatore deve sapere quando gli specificatori di dichiarazione e (cioè la “sezione tipo” e quando inizia la sezione dichiaratore (cioè la sezione “nomi”). Ci sono dichiarazioni in cui la sezione tipo è vuota:

     // constructor definitions don't need a type section MyClass::MyClass() { } // conversion function definitions don't need a type section MyClass::operator int() { } 

    Se il nome che hai specificato non è un tipo, la sezione del tipo termina e inizia la sezione del nome. Dicendo T::X dice al compilatore:

    Ora voglio definire T::X

    Si legge da sinistra a destra, quindi penserà di aver dimenticato un punto e virgola quando incontra il typedef . All’interno delle lezioni l’interpretazione è leggermente diversa ma molto simile a questo. Questa è un’analisi semplice ed efficace.

    Lo stesso argomento vale anche per il nuovo.

    Tendo ad essere d’accordo con te qui. Sintatticamente dovrebbe essere inequivocabile se si lasciano le parentesi. Siccome non ho mai scritto un parser C ++, potrebbero esserci delle insidie ​​nascoste che non vedo.

    Ogni aggiunta di typename in casi d’angolo del linguaggio come in new richiederà potenzialmente una notevole quantità di design sia per i compilatori che per gli scrittori di standard, mentre richiede ancora il typename per la maggior parte degli altri casi in cui è necessario. Non penso che questo ripaga.

    Allora perché i compilatori hanno ancora bisogno della parola chiave typename?

    Perché le regole del linguaggio sono formulate in questo modo: ogni nome dipendente che viene utilizzato in un contesto di tipo deve essere preceduto da typename ( mutatis mutandis , la stessa cosa vale per nome del template e template keyword). Ora, perché le regole del linguaggio non fanno la differenza tra i casi in cui è necessario il nome del tipo per rimuovere l’ambiguità e quelli in cui il contesto fornisce informazioni sufficienti? Probabilmente – non ero presente quando la decisione è stata presa – per mantenere la descrizione della lingua ancora più complessa (considerate le conseguenze dei casi mancanti, in un modo o nell’altro).

    Nel tuo esempio, X non è un nome di tipo (questa possibilità se è compatibile con C in cui il nome del tag non è automaticamente il nome del tipo), quindi devi usare struct :

     pX = new struct T::X; 

    Il tuo codice sembra andare in un’area molto grigia.

    Questo paragrafo sul nascondiglio del nome

    Un nome di class (9.1) o un nome di enumerazione (7.2) può essere nascosto dal nome di una variabile, un membro dati, una funzione o un enumeratore dichiarato nello stesso ambito. Se un nome di class o di enumerazione e una variabile, un membro dati, una funzione o un enumeratore sono dichiarati nello stesso ambito (in qualsiasi ordine) con lo stesso nome, il nome della class o dell’enumerazione è nascosto ovunque la variabile, il membro dati, la funzione o il nome dell’enumeratore è visibile.

    sembra indicare che il compilatore ha ragione nel lamentarsi che A::X non è un nome di tipo.