Je joue avec clang depuis un moment, et je suis tombé sur "test/SemaTemplate/dependent-template-recover.cpp" (dans la distribution de clang) qui est censé fournir des conseils pour récupérer d'une erreur de modèle.
Le tout peut être facilement réduit à un exemple minimal:
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>();
}
};
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<U>();
^
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 suffixe, ou après spécificateur de nom imbriqué dans un identifiant qualifié, et l'expression de suffixe ou l'identifiant qualifié dépend explicitement d'un paramètre de modèle (14.6.2), le membre le nom du modèle doit être préfixé par le mot-clé modèle. Sinon, le nom est supposé nommer un non-modèle.
Dans t->f0<U>();
f0<U>
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
, donc la spécialisation de modèle de membre doit être préfixée par template
mot clé.
Modifiez donc t->f0<U>()
en t->template f0<U>()
.
En plus des points soulevés par d'autres, notez que parfois le compilateur ne pouvait pas se décider et que les deux interprétations peuvent produire des programmes valides alternatifs lors de l'instanciation
#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;
}
Ceci imprime 0
En omettant template
avant f<int()>
mais 1
En l'insérant. Je le laisse comme un exercice pour comprendre ce que fait le code.
Insérez-le juste avant le point où se trouve le curseur:
template<typename T, typename U, int N> struct X {
void f(T* t)
{
t->template f0<U>();
}
};
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 regardent généralement pas le reste de l'expression.[Modifier: voir le commentaire] La raison du mot-clé est la même que la raison pour laquelle vous avez besoin du mot-clé typename
pour indiquer les noms de types dépendants: il dit au compilateur "hé, l'identifiant que vous êtes sur le point de voir est le nom d'un modèle, plutôt que le nom d'un membre de données statique suivi d'un signe inférieur à ".
Extrait de modèles C++
La construction .template Un problème très similaire a été découvert après l'introduction du nom de type. Prenons l'exemple suivant en utilisant le type de jeu de bits standard:
template<int N>
void printBitset (std::bitset<N> const& bs)
{
std::cout << bs.template to_string<char,char_traits<char>,
allocator<char> >();
}
La construction étrange dans cet exemple est .template. Sans cette utilisation supplémentaire du modèle, le compilateur ne sait pas que le jeton inférieur à (<) 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) doivent être utilisées uniquement à l'intérieur des templates et uniquement si elles suivent quelque chose qui dépend d'un paramètre de template.