Секрет заключается в том, что шаблон может быть специализирован для некоторых типов. Это означает, что он также может определять интерфейс совершенно по-разному для нескольких типов. Например, вы можете написать:
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
before. Правильный шаблон выглядит так
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Итог: вы должны добавлять typename
раньше всякий раз, когда вы используете вложенный тип шаблона в своих шаблонах. (Конечно, только если параметр шаблона вашего шаблона используется для этого внутреннего шаблона.)