typename起到一个向编译器通知一个未知标识符是类型的作用,并且仅在模板定义中使用该关键字。还有十分重要的一点:若该名称是依赖于模板参数的限定名,则必须使用该关键字;若限定名不是依赖项,则该名称是可选的。
1.typename 可由任何类型在模板声明或定义中的任何位置使用。不允许在基类列表中使用该关键字,除非将它用作模板基类的模板参数:
1 2 3 4 5 6 |
template <class T> class C1 : typename T::InnerType // Error - typename not allowed. {}; template <class T> class C2 : A<typename T::InnerType> // typename OK. {}; |
2.typename 关键字与class关键字在模板参数列表中是等价的。 例如,以下语句是相同的:
1 2 |
template<class T1, class T2>... template<typename T1, typename T2>... |
3.若该名称是依赖于模板参数的限定名,则必须使用该关键字
首先是两个概念:
(1)qualified name
例如,std::cout;这样含有作用域符号(::)的就是限定名,当我们用using声明将cout引入到当前作用域之后就可以直接使用,这个时候cout就不是限定名了。
(2)dependent name
dependent name是依赖于模板参数的类型,例如:
1 2 3 4 5 6 7 8 9 10 |
template <typename T> class X { int i; std::vector<int> ivec; std::vector<int>::iterator iter; T type; std::vector<T> tvec; std::vector<T>::iterator titer; }; |
前3个成员变量是不依赖于模板参数,所以是non-dependent name,后3个是dependent name,直到实例化该模板的时候才会知道到底是什么类型。
那么问题来了,先看如下示例
1 2 3 4 5 |
template <typename T> class Y { T::iterator *iter; ... }; |
我们可能本意是想定义一个迭代器对象,例如我们如果用vector<int>来实例化这个模板,那么iter 则应该是一个迭代器指针,但是,如果我们用下面这个类来实例化这个模板:
1 2 3 4 5 |
class cType { static int iterator; ... }; |
那么T::iterator *iter会被编译器解释为两个数相乘。
(3)为了避免这个问题,当我们使用qualified dependent name的时候,需要用typename来指出这是一个类型名。即:
1 2 3 4 5 6 |
template <typename T> class Y { typename T::iterator *iter; typedef typename T::iterator iterator; //定义了Y::iterator类型名称 ... }; |
(4)再举一个网络上的例子:
1 2 3 4 5 6 7 8 9 |
/* 我觉得下面这个例子最有用了!记住好处多多! */ // typename.cpp template<class T> class X { typename T::Y m_y; // treat Y as a type T::Y=SomeValue;//treat Y as a Value }; |
参考:
MSDN–typename关键字
https://my.oschina.net/u/1443582/blog/195255