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>
. reference
Typ wygląda zupełnie inaczej niż u innych T
. Wprawdzie nie zmienia rodzaju reference
z typu na coś innego, ale może się zdarzyć.
Co się stanie, jeśli napiszesz własne szablony przy użyciu tego test
szablonu. 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>::ptr
jest to typ. Ale kompilator tego nie wie, a nawet norma radzi mu, aby oczekiwał czegoś przeciwnego, test<T>::ptr
nie jest typem. Aby powiedzieć kompilatorowi, czego się spodziewasz, musisz dodać typename
przed. 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ć typename
przed 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).