1、typename关键字
在声明template参数时, 前缀关键字class和typename可以互换,但在使用模板参数T的内部类型名称即嵌套从属名称时只能用typename。
在C++标准化的过程中,引入关键字typename
是为了说明:模板类型参数内部的标识符(associated type,常见于STL中的各种容器)也可以是一个类型:
比如:
template<typename T>
class MyClass
{
typename T::SubType* ptr;
}
这里介绍模板内参数名称的几个概念;
从属名称(dependent names): 模板(template)内出现的名称, 依赖于某个模板(template)参数, 如T t;
嵌套从属名称(nested dependent names):从属名称在class内呈嵌套装, 如T::const_iterator ci;
非从属名称(non-dependent names): 不依赖任何template参数的名称, 如int value;
任何时候在模板(template)中指涉一个嵌套从属类型名称, 需要在前一个位置, 添加关键字typename;
如果不特定指出typename, 嵌套从属名称, 有可能产生解析(parse)歧义,可能会报错(GCC): error: need 'typename' before 'T::xxx' because 'T' is a dependent scope。
比如:
template<typename T>
class MyClass
{
/*typename*/ T::SubType* ptr;
}
上述程序中,第二个typename被用来说明,SubType是定义与类T内部的一种类型,也就是associated type,因而,ptr是一个指向T::SubType类型的指针。如果不使用typename,T::SubType会被优先看做T的一个静态成员,也就是一个具体而变量或对象,于是,下面的表达式:
T::SubType* ptr;
编译器此时就无法辨别这SubType是什么,因为SubType可能是模板参数T内的一个static变量,ptr可以看成一个全局变量,此时代码会被看做类T的静态成员SubType和ptr的乘积,或者SubType可能是一个typedef比如
class Class_T{
typedef int SubType;
...
};
那上面代码转化过来就是这样:
int *x;
2、嵌套从属名称使用typename的几个场景
a、模板内出现的名称如果依赖于某个模板参数,称之为从属名称(dependent name)。如果从属名称在class内呈嵌套状,我们称为嵌套从属名称(nested dependent name),举例如下:
template<typename T>
void print(const T & container)
{
T::const_iterator iter(container.begin());
cout << *iter << endl;
int value = *iter;
return;
}
- 在上述代码中,iter 的类型是依赖于模板参数T的,因此被称为 从属名称;
- 同理,value的类型是语言内置类型,不依赖于任何模板参数,因此被称为 非从属名称;
- C++编译器在面对从属名称时,如果此时该从属名称又嵌套了其他类型,如此处的 iter就是T::const_iterator类型,这里的T::const_iterator 称为嵌套从属类型名称(嵌套于T类型,从属于模板参数T)
或者
template<typename T> // typename allowed (as is "class")
void f(const T& container, // typename not allowed
typename T::iterator iter); // typename required
上述的T并不是嵌套从属类型名称 (它并非嵌套与任何“取决于模板参数”的东西内),所以声明container时并不需要以typename为前导。
但T::iterator是个嵌套从属类型名称,所以必须以typename为前导。
b、某个模板类里面typedef类型时,如果这个类型与模板参数T相关,就需要使用typename。即这样的形式:
template <class T>
class Test
{
public:
typedef map<int, T> TEMPLATE_MAP; //TEMPLATE_MAP不需要typename,因为它不依赖其他的名称
typedef map<int, T>::iterator TEMPLATE_MAP_ITER; //error! 嵌套从属名称,依赖其他类的iterator名称
typedef typename map<int, T>::iterator TEMPLATE_MAP_ITER; //yes
};
c、例外:嵌套从属类型名称, 如果是基类列表(base class list)和成员初值列(member initialization list)中,不使用typename;
/*
* BInsertSort.cpp
*
* Created on: 2014.4.17
* Author: Spike
*/
#include <iostream>
#include <vector>
using namespace std;
struct Number {
Number(int x) {
std::cout << "Number = " << x << std::endl;
}
};
template<typename T>
struct Base{
typedef Number Nested;
};
template<typename T>
class Derived: public Base<T>::Nested { //不用typename
public:
explicit Derived(int x) : Base<T>::Nested(x) { //不用typename
typename Base<T>::Nested temp(7); //必须使用
}
};
int main () {
Derived<int> d(5);
return 0;
}