Erreur de modèle déroutant

J’ai joué avec clang un moment, et je suis tombé sur “test / SemaTemplate / dependent-template-recover.cpp” (dans la dissortingbution de clang) qui est censé fournir des conseils pour récupérer d’une erreur de modèle.

L’ensemble peut être facilement réduit à un exemple minimal:

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

Le message d’erreur généré par clang:

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

… Mais j’ai du mal à comprendre où exactement on est censé insérer le mot-clé template pour que le code soit syntaxiquement correct?

ISO C ++ 03 14.2 / 4:

Lorsque le nom d’une spécialisation de modèle de membre apparaît après. ou -> dans une expression de postfixe, ou après un spécificateur de nom nested dans un identificateur qualifié, et l’expression de postfix ou l’ID qualifié dépend explicitement d’un paramètre de modèle (14.6.2), le nom du modèle de membre doit être préfixé par le modèle de mot-clé . Sinon, le nom est supposé nommer un non-modèle.

Dans t->f0(); f0 est une spécialisation de modèle de membre qui apparaît après -> et qui dépend explicitement du paramètre de modèle U , de sorte que la spécialisation de modèle de membre doit être préfixée par le mot clé de template .

Changez donc t->f0() en t->template f0() .

En plus des points soulevés par les autres, notez que parfois le compilateur ne peut pas se décider et que les deux interprétations peuvent générer des programmes valides lors de l’instanciation.

 #include  template struct A { typedef int R(); template static U *f(int) { return 0; } static int f() { return 0; } }; template bool g() { A a; return !(typename A::R*)af(0); } int main() { std::cout << g() << std::endl; } 

Ceci imprime 0 en omettant template avant f mais 1 en l'insérant. Je laisse cela comme un exercice pour comprendre ce que le code fait.

Insérez-le juste avant le point où se trouve le curseur:

 template struct X { void f(T* t) { t->template f0(); } }; 

Edit: la raison de cette règle devient plus claire si vous pensez comme un compilateur. Les compilateurs ne regardent généralement en avant qu’un ou deux jetons à la fois, et ne se tournent généralement pas vers le rest de l’expression. [Edit: see comment] La raison du mot-clé est la même que typename mot-clé typename pour indiquer les noms de type dépendants: il indique au compilateur “hé, l’identifiant que vous allez voir est le nom d’un modèle, plutôt que le nom d’un membre de données statique suivi d’un signe moins que “.

Extrait des modèles C ++

La construction .template Un problème très similaire a été découvert après l’introduction de typename. Prenons l’exemple suivant en utilisant le type de bits standard:

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

La construction étrange dans cet exemple est .template. Sans cette utilisation supplémentaire de template, le compilateur ne sait pas que le jeton moins (<) qui suit n'est pas vraiment "inférieur à" mais le début d'une liste d'arguments de modèle. Notez que cela ne pose problème que si la construction avant la période dépend d'un paramètre de modèle. Dans notre exemple, le paramètre bs dépend du paramètre de modèle N.

En conclusion, la notation .template (et les notations similaires telles que -> template) ne doivent être utilisées que dans les modèles et uniquement si elles suivent quelque chose qui dépend d’un paramètre de modèle.