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 reference
type est complètement différent de celui des autres T
s. Certes, cela ne change pas le genre de reference
type à 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 test
modè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>::ptr
soit 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>::ptr
n'est pas un type. Pour dire au compilateur ce que vous attendez, vous devez ajouter un typename
avant. 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 typename
avant 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.)