2013-08-20 14 views
12

Mi sono imbattuto in questo strano comportamento durante il test se è richiesto o meno typename da clang. Sia clang che gcc accettano questo codice mentre msvc lo rifiuta.È possibile omettere il typename nello specificatore di tipo di una definizione di membro fuori linea?

template<class T1> 
struct A 
{ 
    template<class T2> 
    struct B 
    { 
     static B f; 
     static typename A<T2>::template B<T1> g; 
    }; 
}; 

template<class T1> 
template<class T2> 
typename A<T2>::template B<T1> // ok, typename/template required 
    A<T1>::B<T2>::g; 

template<class T1> 
template<class T2> 
A<T1>::B<T2> // clang/gcc accept, msvc rejects missing typename 
    A<T1>::B<T2>::f; 

In generale, un qualificato-id A<T1>::B<T2> (dove A<T1> è un nome dipendente) dovrebbe essere scritto typename A<T1>::template B<T2>. Il comportamento di gcc/clang è errato o c'è un'eccezione alla regola generale (citata sotto) in questo caso particolare?

Si potrebbe sostenere che A<T1> non è un nome dipendente o che B<T2> fa riferimento a un membro dell'istanza corrente. Tuttavia, al momento di analizzare l'identificatore del tipo non è possibile sapere che l'istanza corrente è A<T1>. Sembra problematico richiedere l'implementazione per indovinare che A<T1> è l'istanza corrente.

14,6 risoluzione dei nomi [temp.res]

un nome usato in una dichiarazione modello o definizione e che dipende da un modello-parametro è non assunto per citarne tipo a meno che il nome applicabile ricerca trova un nome di tipo o il nome è qualificato dalla parola chiave typename.

14.2 Nomi di specializzazioni template [temp.names]

Quando il nome di una specializzazione template membro appare dopo . o -> in un suffisso-espressione o dopo un nested-nome-identificatore in un qualificato-id e l'espressione dell'oggetto o del puntatore dell'espressione postfisso o dello specificatore di nome nidificato nell'ID qualificato dipende da un parametro del modello (14.6.2) ma non fa riferimento a un membro dell'istanza corrente (14.6. 2.1), il nome del modello membro deve essere preceduto dal modello della parola chiave . Altrimenti si suppone che il nome indichi un non-modello.

Per indagare ulteriormente ciò che clang sta facendo qui, ho anche provato questo:

template<class T1> 
struct C 
{ 
    template<class T2> 
    struct D 
    { 
     static typename A<T1>::template B<T2> f; 
     static typename A<T1>::template B<T2> g; 
    }; 
}; 

template<class T1> 
template<class T2> 
typename A<T1>::template B<T2> // ok, typename/template required 
    C<T1>::D<T2>::f; 

template<class T1> 
template<class T2> 
A<T1>::B<T2> // clang rejects with incorrect error 
    C<T1>::D<T2>::g; 

Clang dà error: redefinition of 'g' with a different type, ma il tipo di g realtà corrisponda alla dichiarazione.

Mi piacerebbe invece vedere una diagnostica che suggerisce l'uso di typename o template.

Ciò dà credito all'ipotesi che il comportamento di clang nel primo esempio non sia intenzionale.

+0

io personalmente sarebbe aggiungere il 'typename' ... ma non hanno l'unità per scavare nello standard proprio ora :) –

+0

@dribeas Nessun problema;). Devi essere stanco di tutte queste domande di avvocato linguistico ormai! – willj

+6

Indipendentemente dal fatto che sia giusto o sbagliato, ti meriti un upvote solo per trovare * qualsiasi * codice accettato da gcc e clang, ma VC++ rifiuta sulla base di un 'typename 'mancante. –

risposta

1

clang e gcc sono corretti.

Il compilatore conosce A<T1>::B<T2> fa riferimento a un tipo e B<T2> è un modello e che A<T1>::B<T2>::f è un membro dell'istanza corrente. Pertanto, le parole chiave typename e template non sono necessarie.

Da v14.6.2.1P4:

Un nome è un membro della istanziazione corrente se è

A qualificata-id in cui il nested-nome-specificatore riferisce alla istanza corrente e che, quando guardato, si riferisce ad almeno un membro del istanziazione corrente

A<T1>::B<T2> è un qualificato-id e A<T1>:: è il nested-nome-specificatore che si riferisce alla istanziazione corrente. Sappiamo che si riferisce alla A<T1>:: l'istanza corrente 14.6.2.1p1:

Un nome si riferisce l'istanza corrente se è

- nel la definizione di un modello di classe primario o un membro di un modello classe primaria, il nome del modello classe seguito dal modello di elenco argomento del modello primario (come descritto sotto) racchiusi in <> (o un modello equivalente ali la specializzazione),

Nel codice, abbiamo una definizione di un membro di un modello di classe primaria, vale a dire A<T1>::B<T2>::f, e A<T1> è il nome del modello di classe seguito dal all'elenco dei modelli argomento del modello primario.

Nella tua domanda, dici However, at the point of parsing the type-specifier it's not possible to know that the current instantiation is A<T1>. Tuttavia, non posso seguirlo perché il nome A<T1> fa riferimento all'attuale istanza come sopra indicato.

+0

Le regole che richiedono 'typename' (e' template') consentono al parser di sapere se un nome dipendente è un tipo o un modello. Il parser avanza attraverso un flusso di token e deve decidere se un nome è un tipo (o un modello) nel punto in cui viene rilevato un token. Per decidere se 'A :: B' chiama un modello di classe senza la parola chiave' typename', il parser dovrebbe guardare avanti un numero (potenzialmente illimitato) di token per scoprire che 'A ' è l'istanza corrente. – willj

+0

@willj: mentre la prima parte del commento è corretta, la seconda non lo è. Alcune forme del nome identificano l'istanza corrente. In questo caso, 'A ', e' :: A 'entrambi denominano l'istanza corrente. Il compilatore identifica l'istanza corrente con la sintassi, vedi 14.6.2.1p3 per esempi. –

+0

Vedere [questo esempio] (http://coliru.stacked-crooked.com/view?id=3fe1c0107d63f27f28792d7678c32b3a-25dabfc2c190f5ef027f31d968947336) e i messaggi di errore. Il compilatore ** sa ** 'A :: B' assegna un nome a un modello di classe perché lo ha già cercato. –

1

MSVC è corretto.

La mia lettura dello standard C++ 11 suggerisce che è richiesto typename.

Senza la parola chiave typename, si presume che un nome dipendente non assegni un nome a un tipo.

14,6 risoluzione dei nomi [temp.res]

2) un nome usato in una dichiarazione modello o definizione e che dipende da un modello-parametro è non assunto per citarne tipo a meno che il caso la ricerca del nome trova un nome di tipo o il nome è qualificato con la parola chiave typename.

3) Quando un qualificato-id è inteso riferirsi ad un tipo che non è un membro del istanziazione corrente e il suo nested-nome-specificatore si riferisce ad un tipo di carico, esso è preceduto dalla parola chiave typename

7) nella definizione di un modello di classe o nella definizione di un membro di una classe template seguito il dichiaratore-id, la parola chiave typename non è richiesto quando ci si riferisce al nome di un precedentemente dichiarato membro del modello di classe che dichiara un tipo.[Nota: tali nomi possono essere trovati utilizzando nome non qualificato ricerca, i membri della classe di ricerca in l'istanza corrente, o membro della classe di accesso espressione di ricerca quando il tipo dell'espressione oggetto è l'istanza corrente

14.6.2.1 Dependent tipi [temp.dep.type]

a nome si riferisce al istanziazione corrente se è

  • nella definizione di un modello di classe primaria o un membro di un primario template di classe, il nome del modello classe seguito dall'elenco template argomento del modello primario (come descritto sotto) racchiusi in <>

Quando A<T1> è utilizzato nella definizione di un membro di A , si riferisce all'istanziazione corrente . Quando si analizza la definizione di f, è possibile trovare un nome di tipo qualificato da A<T1>:: tramite ricerca nome membro di classe nell'istanza corrente.

Tuttavia, quando il parser C++ incontra A<T1> nel tipo restituito di una definizione di funzione membro, prima dell'idificatore-dichiaratore, non ha ancora incontrato il nome della classe che lo include. Il parser non può determinare se A si riferisca o meno alla classe di chiusura a questo punto.

Per questo motivo - indipendentemente dal fatto che A<T1> nominali o meno l'istanza corrente - lo standard non consente l'omissione di typename all'interno della definizione di un membro di un modello di classe prima del dichiaratore-id.

Questo example da Vaughn Catone dimostra che il comportamento di Clang/GCC è incoerente, e richiede typename in uno scenario simile:

template <typename T> 
struct A { 
    typedef int X; 
    X f(); 
}; 

template <typename T> 
A<T>::X A<T>::f() // error: missing 'typename' 
{ 
} 
Problemi correlati