Le secret réside dans le fait qu'un modèle peut être spécialisé pour certains types. Cela signifie qu'il peut également définir l'interface complètement différente pour plusieurs types. Par exemple, vous pouvez écrire:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
On pourrait se demander pourquoi est-ce utile et en effet: cela semble vraiment inutile. Mais gardez à l'esprit que, par exemple, std::vector<bool>le referencetype est complètement différent de celui des autres Ts. Certes, cela ne change pas le genre de referencetype à quelque chose de différent, mais cela peut néanmoins arriver.
Maintenant, que se passe-t-il si vous écrivez vos propres modèles en utilisant ce testmodèle. Quelque chose comme ça
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
cela semble vous convenir car vous vous attendez à ce que ce test<T>::ptrsoit un type. Mais le compilateur ne sait pas et en fait il est même conseillé par la norme d'attendre le contraire, ce test<T>::ptrn'est pas un type. Pour dire au compilateur ce que vous attendez, vous devez ajouter un typenameavant. Le modèle correct ressemble à ceci
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Bottom line: Vous devez ajouter typenameavant chaque fois que vous utilisez un type imbriqué de modèle dans vos modèles. (Bien sûr, uniquement si un paramètre de modèle de votre modèle est utilisé pour ce modèle interne.)