一、typename关键字
在声明template参数时, 前缀关键字class和typename能够互换,但在使用模板参数T的内部类型名称即嵌套从属名称时只能用typename。ios
在C++标准化的过程当中,引入关键字typename
是为了说明:模板类型参数内部的标识符(associated type,常见于STL中的各类容器)也能够是一个类型:ui
好比:spa
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;code
若是不特定指出typename, 嵌套从属名称, 有可能产生解析(parse)歧义,可能会报错(GCC): error: need 'typename' before 'T::xxx' because 'T' is a dependent scope。对象
好比:blog
template<typename T> class MyClass { /*typename*/ T::SubType* ptr; }
上述程序中,第二个typename被用来讲明,SubType是定义与类T内部的一种类型,也就是associated type,于是,ptr是一个指向T::SubType类型的指针。若是不使用typename,T::SubType会被优先看作T的一个静态成员,也就是一个具体而变量或对象,因而,下面的表达式:ci
T::SubType* ptr;
编译器此时就没法辨别这SubType是什么,由于SubType多是模板参数T内的一个static变量,ptr能够当作一个全局变量,此时代码会被看作类T的静态成员SubType和ptr的乘积,或者SubType多是一个typedef好比编译器
class Class_T{ typedef int SubType; ... };
那上面代码转化过来就是这样:it
int *x;
二、嵌套从属名称使用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; }