Sekret polega na tym, że szablon może być wyspecjalizowany dla niektórych typów. Oznacza to, że może również definiować interfejs zupełnie inny dla kilku typów. Na przykład możesz napisać:
template<typename T>
struct test {
typedef T* ptr;
};
template<>
struct test<int> {
T* ptr;
};
Ktoś mógłby zapytać, dlaczego jest to przydatne i rzeczywiście: to naprawdę wygląda na bezużyteczne. Ale pamiętaj, że np std::vector<bool>. referenceTyp wygląda zupełnie inaczej niż u innych T. Wprawdzie nie zmienia rodzaju referencez typu na coś innego, ale może się zdarzyć.
Co się stanie, jeśli napiszesz własne szablony przy użyciu tego testszablonu. Coś takiego
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
wydaje się to być dla ciebie w porządku, ponieważ oczekujesz, że test<T>::ptrjest to typ. Ale kompilator tego nie wie, a nawet norma radzi mu, aby oczekiwał czegoś przeciwnego, test<T>::ptrnie jest typem. Aby powiedzieć kompilatorowi, czego się spodziewasz, musisz dodać typenameprzed. Prawidłowy szablon wygląda następująco
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Podsumowując: musisz dodać typenameprzed każdym użyciem zagnieżdżonego typu szablonu w swoich szablonach. (Oczywiście tylko wtedy, gdy parametr szablonu twojego szablonu jest używany dla tego szablonu wewnętrznego).