typename 的用法
Usage
typename 主要有两个作用,让我们先来看看标准手册对该关键字的说明。
- In the template parameter list of a template declaration, typename can be used as an alternative to class to declare type template parameters.
- 在模板声明的模板参数列表中,typename 可以用来替换 class 声明模板参数类型
- Inside a declaration or a definition of a template, typename can be used to declare that a dependent qualified name is a type.
- 在模板的声明或定义中,typename 可以用来声明从属名称是一种类型
声明模板参数类型
以下 tempalate 声明式中,class 和 typename 用什么不同?
template <class T>
class Qgw;
template <typename T>
class Qgw;
答案:完全一样。标准中说 typename 可以用来替换 class 声明模板参数类型,并没有说在此时有什么不同。
声明嵌套从属名称
在了解这个作用前,我们需要先学习两种名称,从属名称(dependent names)和嵌套从属名称(nested dependent name)。
让我们来看这样一段代码,代码本身并没有实际意义。
// C 接收一个 STL 容器类型
// 这份代码并不正确
template <class C>
void Test(C& container)
{
C w;
C::iterator iter(container.begin());
}
在上述代码中有两个局部变量 w 和 iter。w 的类型是 C,实际是什么取决于 template 参数 C。template 内出现的名称如果依赖于某个 template 参数称之从属名称。如果从属名称在 class 内呈嵌套状,就称为嵌套从属名称,像 iter 的类型为 C::iterator,就是一个嵌套从属名称。
嵌套从属名称有可能导致解析困难,先来看个比较极端的例子:
template <class C>
void Test(C& container)
{
C::iterator* x;
...
}
上述代码我们声明了一个局部变量 x,它是个指针,指向一个 C::iterator。但它之所以被这么认为,是因为我们已经知道 C::iterator 是个类型。如果 C::iterator 不是个类型呢?如果 C 有个 static 成员变量而又刚好叫 iterator,或者 x 是个全局变量呢?那样的话上述代码不再是声明一个局部变量,而是一个相乘动作。
在我们知道 C 是什么之前,没有任何办法可以知道 C::iterator 是否是一个类型。C++ 有个规则可以解析这一歧义状态:如果解析器在 template 中遇到一个嵌套从属名称,它便假设这名称不是一个类型,除非你明确指出它是一个类型。所以缺省情况下嵌套从属名称不是类型,有两个例外会在下面指出。
我们可以用 typename 来明确指出嵌套从属名称是一个类型,标准中写到 typename 可以用来声明从属名称是一种类型。于是我们可以这样修改代码:
template <class C>
void Test(C& container)
{
C w;
typename C::iterator iter(container.begin());
typename C::iterator* x;
}
一个简单的规则:任何时候当你想在 template 中指涉一个嵌套从属名称,就必须在它的前一个位置放上关键字 typename。
typename 只能被用来验明嵌套从属名称,其他名称不该有它存在。
template <class C>
void Test(const C& container, // 不允许使用 typename,vs 下没报错,g++ 报错了
typename C::iterator iter); // 一定要使用 typename
例外
typename 不可以出现在 base classes list 内嵌套从属名称之前,也不可以在 member initialization list(成员初始化列表)中作为 base class 修饰符。例如:
temalate <class T>
class Derived : public Base<T>::Nested // base classes list 中不允许 typename
{
public:
Derived (int x)
: Base<T>::Nested(x) // mem. init. list 中不允许 typename
{
typename Base<T>::Nested temp; // 既不在 base classes list 也不在 mem. init. list 需要加 typename
}
}