При использовании шаблонов иногда надо писать typename у типов - когда и зачем это делать? template inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="") { typename T::const_iterator pos std::cout << optcstr for (pos=coll.begin() pos!=coll.end() ++pos) { std::cout << *pos << ' ' } std::cout << std::endl } <br> Ответ Проблема в том, что T::const_iterator — зависимое имя: оно зависит от параметра шаблона T. В этой точке компилятор не знает, каким будет T, и не может предсказать, будет ли T::const_iterator именем типа или, например, именем статического поля или вообще шаблона. Поэтому он и не пытается угадать, и предполагает, что это поле. Если же ему подсказать, он будет предполагать, что T::const_iterator — это тип, и поймёт, что typename T::const_iterator pos — объявление переменной. Почему же компилятор не может подождать с выяснением смысла выражения T::const_iterator до того момента, когда тип T будет уже известен (то есть, до момента разворачивания шаблона с конкретным типом T)? А вот почему: на момент применения шаблона тип T имеет право быть ещё не определён! И ещё он может зависеть от самого шаблона. Так что откладывать выяснение смысла выражения нельзя. Пример: template class comparable { bool compare(T& other) { return this == other } } class length : public comparable // в этой точке для comparable // тип T ещё не известен полностью! { ...
Пример кода, иллюстрирующего «скользкие» моменты, приведён ниже. Он не компилируется gcc (так как нету полагающегося по стандарту typename), но более либеральный MSVC 2012 его компилирует и выполняет. typename для того и нужен, чтобы исключить подобные сюрпризы. #include "stdafx.h" // нужно для MSVC #include using namespace std template struct A { void f() { // если T::iterator - тип, это предварительное объявление функции // если T::iterator - число, это объявление переменной с инициализацией int x(T::iterator) } void g() { int x = 5 { // если T::iterator - шаблон, принимающий числовой аргумент, // это инстанциация шаблона в переменную x, перекрывающую x // за фигурными скобками // если T::iterator -- экземпляр класса с перегруженным оператором <, // это сравнение T::iterator с нулём, а затем сравнение результата // со значением переменной x! T::iterator<0> x // Кто-то всё ещё сомневается, что C++ - непредсказуемый язык? } } } struct T1 { typedef int iterator } struct T2 { static const int iterator = 5 } struct T3 { template struct iterator { iterator() { cout << "constructing template with C = " << C << endl } } } <br>struct T4 { struct Titerator { Titerator operator < (int value) { cout << "in operator < " << value << endl return Titerator() } bool operator > (int value) { cout << "in operator > " << value << endl return false } } static Titerator iterator } <br>T4::Titerator T4::iterator = T4::Titerator() int main(int argc, char* argv[]) { A a1 a1.f() A a2 a2.f() A a3 a3.g() A a4 a4.g() return 0 } Результат работы таков: constructing template with C = 0 in operator < 0 in operator > 5