2010-09-24 12 views
68

Ho giocato con clang un po 'di tempo e sono incappato in "test/SemaTemplate/dependent-template-recover.cpp" (nella distribuzione clang) che dovrebbe fornire suggerimenti per il ripristino da un modello errore.Errore di modello di confusione

Il tutto può essere facilmente messo a nudo fino a un esempio minimo:

template<typename T, typename U, int N> struct X { 
    void f(T* t) 
    { 
     // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}} 
     t->f0<U>(); 
    } 
}; 

Il messaggio di errore derivanti dai clang:

tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name 
     t->f0<U>(); 
      ^
      template 
1 error generated. 

... Ma ho un momento difficile comprensione esattamente dove si suppone che si debba inserire la parola chiave template in modo che il codice sia sintatticamente corretto?

+9

Hai provato a inserirli dove punta la freccia? –

+3

Simile a [questo] (http://stackoverflow.com/questions/3691420/compiler-error-when-using-integer-as-template-parameter/) e [this] (http://stackoverflow.com/questions/3621719/c-template-syntax) –

risposta

65

ISO C++ 03 14,2/4:

Quando il nome di una specializzazione template membro appare dopo. o -> in un'espressione postfix, o dopo identificatore nome nested in un id qualificato, e l'espressione postfissa o id qualificato dipende esplicitamente da un parametro template (14.6.2), il nome modello membro deve essere preceduto dal modello di parola chiave. Altrimenti si suppone che il nome indichi un non-modello.

In t->f0<U>();f0<U> è un modello di specializzazione membro che appare dopo ->, e che in modo esplicito dipende dal parametro modello U, in modo che il modello di specializzazione membro deve essere preceduto da template parola chiave.

Quindi modificare t->f0<U>() a t->template f0<U>().

+0

È interessante notare che ponendo l'espressione tra parentesi: 't -> (f0 ())' l'avrei corretto, come pensavo avrebbe messo 'f0 ()' in standalone espressione ... beh, ho pensato che fosse sbagliato, sembra ... –

+5

Potresti commentare perché questo è il caso? Perché il C++ richiede questo tipo di sintassi? – Curious

7

inserirla appena prima del punto in cui il cursore è:

template<typename T, typename U, int N> struct X { 
    void f(T* t) 
    { 
     t->template f0<U>(); 
    } 
}; 

Edit: la ragione di questa regola diventa più chiaro se si pensa come un compilatore. I compilatori in genere guardano avanti solo uno o due token contemporaneamente, e generalmente non "guardano avanti" al resto dell'espressione. [Modifica: vedi commento] Il motivo della parola chiave è lo stesso del perché è necessaria la parola chiave typename per indicare i nomi dei tipi dipendenti: sta dicendo al compilatore "hey, l'identificatore che stai per vedere è il nome di un modello, piuttosto che il nome di un membro di dati statici seguito da un segno di meno ".

+0

Avrei ** mai ** potuto indovinarlo ... ma grazie ;-). c'è sempre qualcosa da imparare sul C++! –

+3

Anche con infinito look-ahead, avrai ancora bisogno di 'template'. Ci sono casi in cui entrambi con e senza 'template' produrranno programmi validi con un comportamento diverso. Quindi questo non è solo un problema di sintassi ('t-> f0 (0)' è sintatticamente valido sia per la versione di elenco di argomenti inferiore che di modello). –

+0

@Johannes Schaub - litb: Giusto, quindi è più un problema di assegnare un significato semantico coerente all'espressione, piuttosto che guardare avanti. – Doug

6

Estratto dal C++ Templates

Il .template Construct Un problema molto simile è stato scoperto dopo l'introduzione del typename. Considera l'esempio seguente utilizzando il tipo di set di bit standard:

template<int N> 
void printBitset (std::bitset<N> const& bs) 
{ 
    std::cout << bs.template to_string<char,char_traits<char>, 
             allocator<char> >(); 
} 

Lo strano costrutto in questo esempio è .template. Senza quell'uso extra di template, il compilatore non sa che il token minore (<) che segue non è realmente "meno di" ma l'inizio di un elenco di argomenti del template. Si noti che questo è un problema solo se il costrutto prima del periodo dipende da un parametro del modello. Nel nostro esempio, il parametro bs dipende dal parametro di modello N.

In conclusione, la notazione .template (e notazioni simili come -> modello) deve essere utilizzata solo all'interno di modelli e solo se seguono qualcosa che dipende su un parametro del modello.

+0

anzi, +1 per l'eccellente esempio –

19

Oltre ai punti di altri hanno fatto, notare che a volte il compilatore non poteva fare la sua mente e entrambe le interpretazioni possono produrre programmi valida alternativa quando si crea un'istanza

#include <iostream> 

template<typename T> 
struct A { 
    typedef int R(); 

    template<typename U> 
    static U *f(int) { 
    return 0; 
    } 

    static int f() { 
    return 0; 
    } 
}; 

template<typename T> 
bool g() { 
    A<T> a; 
    return !(typename A<T>::R*)a.f<int()>(0); 
} 


int main() { 
    std::cout << g<void>() << std::endl; 
} 

Questo stampa 0 quando omettendo template prima f<int()> ma 1 quando lo si inserisce. Lo lascio come esercizio per capire cosa fa il codice.

+1

Questo è un esempio diabolico! –

+1

Non riesco a riprodurre il comportamento che stai descrivendo in Visual Studio 2013. Si chiama sempre "f " e stampa sempre '1', il che ha perfettamente senso per me. Ancora non capisco perché sia ​​richiesta la parola chiave 'template' e che differenza faccia. –

+0

@Violet il compilatore VSC++ non è un compilatore C++ compatibile. Una nuova domanda è necessaria se vuoi sapere perché VSC++ stampa sempre 1. –