Errore di modello confuso


91

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

Il tutto può essere facilmente ridotto 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 prodotto da 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 difficoltà a capire dove esattamente si dovrebbe inserire la templateparola chiave per avere il codice sintatticamente corretto?


11
Hai provato a inserirlo dove punta la freccia?
Mike Seymour

Risposte:


104

ISO C ++ 03 14.2 / 4:

Quando il nome di una specializzazione del modello di membro viene visualizzato dopo. o -> in un'espressione postfissa, o dopo l'identificatore-nome-annidato in un id-qualificato, e l'espressione-postfissa o id-qualificato dipende esplicitamente da un parametro-modello (14.6.2), il nome del modello membro deve essere preceduto dal modello di parole chiave . In caso contrario, si presume che il nome denomini un non modello.

In t->f0<U>(); f0<U>è una specializzazione del modello di membro che appare dopo ->e che dipende esplicitamente dal parametro del modello U, quindi la specializzazione del modello di membro deve essere preceduta da una templateparola chiave.

Quindi cambia t->f0<U>()in t->template f0<U>().


È interessante notare che ho pensato di mettere l'espressione tra parentesi: t->(f0<U>())avrei risolto il f0<U>()problema, poiché pensavo che avrebbe messo un'espressione autonoma ... beh, ho pensato male, sembra ...

26
Potresti forse commentare perché questo è il caso? Perché il C ++ richiederebbe questo tipo di sintassi?
Curioso

2
Sì, questo è strano. La lingua può "rilevare" che la parola chiave del modello deve essere presente. Se può farlo, dovrebbe semplicemente "inserire" la parola chiave in essa stessa.
Enrico Borba

26

Oltre ai punti fatti da altri, si noti che a volte il compilatore non riesce a prendere una decisione ed entrambe le interpretazioni possono fornire programmi validi alternativi durante l'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;
}

Viene stampato 0quando si omette templateprima f<int()>ma 1quando lo si inserisce. Lo lascio come esercizio per capire cosa fa il codice.


3
Questo è un esempio diabolico!
Matthieu M.

1
Non riesco a riprodurre il comportamento che descrivi in ​​Visual Studio 2013. Chiama f<U>e stampa sempre 1, il che per me ha perfettamente senso. Continuo a non capire perché la templateparola chiave è richiesta e che differenza fa.
Violet Giraffe

@Violet il compilatore VSC ++ non è un compilatore C ++ conforme. È necessaria una nuova domanda se vuoi sapere perché VSC ++ stampa sempre 1.
Johannes Schaub - litb

1
Questa risposta spiega perché templateè necessario: stackoverflow.com/questions/610245/… senza fare affidamento esclusivamente su termini standard che sono difficili da capire. Si prega di segnalare se qualcosa in quella risposta è ancora confuso.
Johannes Schaub - litb

@ JohannesSchaub-litb: Grazie, un'ottima risposta. Risulta, l'ho letto prima perché era già stato votato da me. A quanto pare, la mia memoria è meh.
Violet Giraffe

12

Inseriscilo appena prima del punto in cui si trova il cursore:

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

Modifica: il motivo di questa regola diventa più chiaro se pensi come un compilatore. I compilatori generalmente 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 motivo per cui hai bisogno della typenameparola chiave per indicare i nomi dei tipi dipendenti: sta dicendo al compilatore "ehi, 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 minore di ".


1
Non avrei mai potuto immaginarlo ... ma grazie ;-). chiaramente c'è sempre qualcosa da imparare sul C ++!

3
Anche con una prospettiva infinita, avrai ancora bisogno template. Ci sono casi in cui sia con che senza templateprodurrà programmi validi con comportamenti diversi. Quindi questo non è solo un problema sintattico ( t->f0<int()>(0)è valido sintatticamente sia per la versione dell'elenco di argomenti minore di che per quella del modello).
Johannes Schaub - litb

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

11

Estratto dai modelli C ++

Il costrutto .template Un problema molto simile è stato scoperto dopo l'introduzione di typename. Considera il seguente esempio 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 questo uso aggiuntivo del modello, il compilatore non sa che il token minore di (<) che segue non è realmente "minore di" ma l'inizio di un elenco di argomenti del modello. Notare 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 del modello N.

In conclusione, la notazione .template (e notazioni simili come -> template) dovrebbe essere usata solo all'interno dei template e solo se seguono qualcosa che dipende da un parametro del template.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.