含义一:模板声明
在 template
的声明式中,可以使用 class
或者 typename
,如下:
template<class T> class Widget;
template<typename T> class Widget;
在这里,class
和 typename
没有任何不同,个人倾向于使用 typename
,避免与类型声明的 class
混淆。
含义二:声明嵌套从属名称类型变量
假设现在有这样一个模板函数,传进来一个模板容器,希望打印出容器内的第二个元素:
template<typename C>
void print2ndElement(const C& container) {
if (container.size() > 2) {
C::const_iterator iter(container.begin());
++iter;
int value = *iter;
std::cout << value;
}
}
乍一看没有什么问题,那么我们考虑假设有另外一种实现:
template<typename C>
void print2ndElement(const C& container) {
C::const_iterator *x;
//...
}
看起来好像是声明了一个局部变量 x
,指向 C::const_iterator
。它之所以会被这样认为,是因为我们心里已经默认了 C::const_iterator
代表一个类型。但是有没有可能 C::const_iterator
并不是一个类型呢?
有!!
如果恰巧 C
中有一个静态变量,就命名为 const_iterator
;
如果 x
恰巧是一个 global variale;
那么,上面那句话就变成了 C
中的静态变量 const_iterator
与 全局变量 x
的相乘动作!
人间惊奇!!
虽然这个事情发生的概率极低,但是确认仍然有可能的,C++解析器必须关心模板类、模板函数所有可能的输入。
所以,如果 C++ 解析器在 template
中遇到了一个嵌套从属名称,它会假设这个名称不是一个类型,除非你告诉它是。我们必须用 typename
关键字,来告诉解析器,这个嵌套名称是一个类型。 有一个例外下面会说明。
所以最开始的代码应该修改为:
template<typename C>
void print2ndElement(const C& container) {
if (container.size() > 2) {
typename C::const_iterator iter(container.begin()); //加上 typename
//...
}
}
嵌套从属名称就是那些在类中定义的别名,比如:
vector<int>::iterator
std::iterator_traits<vector<int>::iterator>::value_type
例外
typename 必须作为嵌套从属名称的前缀词 这个规则的例外是,typename 不能出现在 base class 内的嵌套从属名称之前,也不能出现在成员初始化列表中作为 base class 的修饰符,如下面所示:
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; //必须加 typename
}
}
用 typedef
定义别名
对于下面这种较长的类型定义:
template<typename IterT>
void wordWithIterator(IterT iter){
typename std::iterator_traits<IterT>::value_type temp;
//..
}
每次定义这种类型时,都必须写那一大串,这个时候应该考虑用用 typedef
关键字定义别名:
template<typename IterT>
void wordWithIterator(IterT iter){
typedef typename std::iterator_traits<IterT>::value_type value_type; //定义别名
value_type temp;
//..
}
这个时候,每次可以直接使用 value_type
定义变量,简洁!