typename的正确使用
一、在声明模板参数时,class 和 typename 是可互换的。
以下完全等价:
template<class T> class Widget;
template<typename T> class Widget;
二、通常情况下,必须用 typename 去标识嵌套依赖类型名,但在基类列表中或在一个构造函数的成员初始化列表中作为一个基类标识符时除外。
template<typename C>
void print(const C& obj)
{
C::const_iterator *x; // 无法编译,带::的默认不是类型名
…
}
}
带::的默认不是类型名。所以,编译器会把C::const_iterator * x理解为C中一个名为const_iterator的变量剩以x。
iter 的类型是 C::const_iterator,一个依赖于模板参数C 的类型。一个模板中的依赖于一个模板参数的名字被称为依赖名字。当一个依赖名字嵌套在一个 class(类)的内部时,我称它为嵌套依赖名字,即::前是模板参数名或依赖于模板参数的类型名时。C::const_iterator是一个 nested dependent name(嵌套依赖名字)。实际上,它是一个 nested dependent type name(嵌套依赖类型名),也就是说,一个涉及到一个 type(类型)的 nested dependent name(嵌套依赖名字)。直到 C 成为已知之前,没有任何办法知道 C::const_iterator 到底是不是一个 type(类型),而当 template(模板)print被解析的时候,C 还不是已知的。
正确的做法:在嵌套依赖名字前用typename明确告诉编译器C::const_iterator是一个 type(类型):
template<typename C> // this is valid C++
void print2nd(const C& container)
{
if (container.size() >= 2) {
typename C::const_iterator * x;
//typedef typename C:: const_iterator ConIt; 这也是一种常见做法,typedef后用typename
}
}
该规则有2个例外:
typename 不必前置于在一个 list of baseclasses(基类列表)中的或者在一个 memberinitialization list(成员初始化列表)中作为一个 base classes identifier(基类标识符)的 nested dependenttype name(嵌套依赖类型名)。例如:
template<typename T>
class Derived: public Base<T>::Nested {
// base class list: typename not
public: // allowed
explicit Derived(int x)
: Base<T>::Nested(x) // baseclass identifier in mem
{
// init. list: typename not allowed
typename Base<T>::Nested temp; // use of nesteddependent type
... // name not in a base class list or
} // as a base class identifier in a
... // mem. init. list: typename required
};