Секрет заключается в том, что шаблон может быть специализирован для некоторых типов. Это означает, что он также может определять интерфейс, совершенно различный для нескольких типов. Например, вы можете написать:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
Кто-то может спросить, почему это полезно и действительно: это действительно выглядит бесполезным. Но имейте в виду, что, например, std::vector<bool>
тип reference
выглядит совершенно иначе, чем для других T
s. По общему признанию это не меняет тип reference
с типа на что-то другое, но, тем не менее, это может произойти.
Теперь, что произойдет, если вы напишите свои собственные шаблоны, используя этот test
шаблон. Как то так
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
вам кажется, что это нормально, потому что вы ожидаете , что test<T>::ptr
является типом. Но компилятор не знает, и на самом деле стандарт даже советует ожидать обратного, test<T>::ptr
не является типом. Чтобы сообщить компилятору, что вы ожидаете, вы должны добавить typename
перед этим. Правильный шаблон выглядит так
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Итог: вы должны добавлять typename
раньше, когда вы используете вложенный тип шаблона в ваших шаблонах. (Конечно, только если для этого внутреннего шаблона используется параметр шаблона вашего шаблона.)