问题:在下面模板声明式中,class和typename有什么不同?
回答:没有什么不同。当我们声明模板类型参数,class和typename的意义完全相同。
然而,C++并不总是把class和typename视为等价。有时候你一定要使用typename。为了解其时机,我们必须先谈谈你可以在模板内refer to的两种名称。
假如我们有个函数模板,接受一个STL兼容容器为参数,容器内持有的对象可以被赋值为ints。进一步假设这个函数只是打印第二个元素值:
template<typename C> // error,不能通过编译
void print2nd(const C& container){
if(container.size(0 >= 2){
C::const_iterator iter(container.begin());
++iter;
int value = *iter;
std::cout << value;
}
}
上面:
- iter的类型是C::const_iterator,实际是什么必须取决于模板参数C。模板内出现的名称如果依赖于某个模板参数,称之为从属名称。如果从属名称在类内呈嵌套状,我们称它为嵌套从属名称。C::const_iterator就是这样一个名称。实际上它还是个嵌套从属类型名称,也就是个嵌套从属名称并且refer to某类型。
- vlaue的类型是int。int是一个并不依赖任何模板参数的名称,也叫做非从属名称
嵌套从属名称可能导致解析困难,举个例子:
template<typename C> // error,不能通过编译
void print2nd(const C& container){
C::const_iterator* x;
}
看起来好像是要声明x为一个local变量,它是个指针,指向一个C::const_iterator。但它之所以被这个认为,只因为我们“已经知道”C::const_iterator是个类型。如果C::const_iterator不是个类型呢?如果C有个static成员变量碰巧被命名为const_iterator,或者如果x碰巧是个全局变量名称呢?那么上面代码就不再是声明一个local变量,而是一个相乘动作:C::const_iterator乘以x
C++有个规则可以消除这一歧义:如果编译期在模板内遭遇一个嵌套从属名称,它便假设这个名称不是个类型,除非你告诉它是。所以缺省情况下嵌套从属名称不是类型。业就是说
template<typename C> // error,不能通过编译
void print2nd(const C& container){
if(container.size(0 >= 2){
C::const_iterator iter(container.begin()); //编译期不会把C::const_iterator当作类型,而是当作一个非类型看待
纠正方法:任何时候当你想在模板中指涉一个嵌套从属类型名称,就必须在它紧邻的前一个位置加上typename(这个规则有一个例外)
template<typename C>
void print2nd(const C& container){
if(container.size(0 >= 2){
typename C::const_iterator iter(container.begin()); //告诉编译期把C::const_iterator当作类型解析
typename只被用来验明嵌套从属名称,其他名称不该有它存在,比如:
template<typename C> // 运行typname或者class
void f(const C& container, //不允许typename
typename C::iterator iter); //一定要typenama
typname必须作为嵌套从属类型名称的前缀词
这一规则的例外是,typename不可以出现在base classes list内的嵌套从属类名称之前,也不可以在成员初始化列表中作为base class修饰符
再看一个例子。假设我们正在写一个函数模板,它接受一个迭代器,我们打算以该迭代器refer to的对象做一份local副本temo:
template<typename IterT>
void workWithIterator<IterT iter>{
typename std::iterator_traits<IterT>::value_type temp(*iter);
}
std::iterator_traits<IterT>::value_type
表示“类型为IterT的对象所指之物的类型”,比如如果IterT的vector<int>::iterator
,temp的类型就是int。因为std::iterator_traits<IterT>::value_type
是个嵌套从属类型参数(value被嵌套在iterator_traits<IterT>
之内而IterT是个模板参数),所以必须在它之前防止typename。
更常见的方法:
template<typename IterT>
void workWithIterator<IterT iter>{
typedef typename std::iterator_traits<IterT>::value_type value_type
value_type temp(*iter);
}
总结:
- 声明模板参数时,class和typename可以互换
- 请使用typename标示嵌套从属类型名称,但不得在base class lists或者member initialization list内以它作为base class修饰符