O segredo está no fato de que um modelo pode ser especializado para alguns tipos. Isso significa que também pode definir a interface completamente diferente para vários tipos. Por exemplo, você pode escrever:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
Alguém pode perguntar por que isso é útil e de fato: Isso realmente parece inútil. Mas lembre-se de que, por exemplo, std::vector<bool>
o reference
tipo parece completamente diferente do que para outros T
s. É certo que não muda o tipo de reference
um tipo para algo diferente, mas, no entanto, pode acontecer.
Agora, o que acontece se você escrever seus próprios modelos usando esse test
modelo. Algo assim
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
parece estar bem para você, porque você espera que esse test<T>::ptr
seja um tipo. Mas o compilador não sabe e, de fato, ele é aconselhado pelo padrão a esperar o contrário, test<T>::ptr
não é um tipo. Para dizer ao compilador o que você espera, adicione um typename
antes. O modelo correto se parece com isso
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Conclusão: você deve adicionar typename
antes sempre que usar um tipo aninhado de modelo nos seus modelos. (Obviamente, apenas se um parâmetro do modelo for usado para esse modelo interno.)