Вам необходимо использовать typename
для так называемых «зависимых типов». Это типы, которые зависят от аргумента шаблона и не известны, пока не будет создан экземпляр шаблона. Это, вероятно, лучше всего объяснить на примере:
struct some_foo {
typedef int bar;
};
template< typename Foo >
struct baz {
typedef Foo::bar barbar; // wrong, shouldn't compile
barbar f(); // would be fine if barbar were a type
// more stuff...
};
То, что typedef
определяет barbar
, это то, что требует typename
для того, чтобы компилятор мог проверить шаблон на наличие явных синтаксических ошибок до того, как будет создан с конкретным типом. Причина в том, что когда компилятор видит шаблон в первый раз (когда он еще не создан с конкретными параметрами шаблона), компилятор не знает, является ли Foo::bar
типом. Насколько мне известно, я мог бы попытаться создать экземпляр baz
с такими типами, как этот
struct some_other_foo {
static int bar;
};
в этом случае Foo::bar
будет ссылаться на объект , а не на тип, и определение baz::bar
будет синтаксической бессмыслицей. Не зная, относится ли Foo::bar
к типу, компилятор не имеет возможности проверить что-либо в пределах baz
, которое прямо или косвенно использует barbar
, даже для самых глупых опечаток, пока не будет создан экземпляр baz
. Использование правильных typename
, baz
выглядит следующим образом:
template< typename Foo >
struct baz {
typedef typename Foo::bar barbar;
barbar f();
// more stuff...
};
Теперь компилятор, по крайней мере, знает, что Foo::bar
должно быть именем типа, что также делает barbar
именем типа. Так что объявление f()
синтаксически тоже нормально.
Кстати, есть аналогичная проблема с шаблонами вместо типов:
template< typename Foo >
struct baz {
Foo::bar<Foo> create_wrgl(); // wrong, shouldn't compile
};
Когда компилятор "видит" Foo::bar
, он не знает, что это такое, поэтому bar<Foo
может быть также и сравнением, оставляя компилятор в замешательстве по поводу конечного >
. Здесь также нужно дать компилятору подсказку, что Foo::bar
должно быть именем шаблона:
template< typename Foo >
struct baz {
Foo::template bar<Foo> create_wrgl();
};
Осторожно: в частности, Visual C ++ по-прежнему не реализует правильный двухфазный поиск (по сути: он на самом деле не проверяет шаблоны, пока они не будут созданы). Поэтому он часто принимает ошибочный код, который пропускает typename
или template
.