使用模板时,可以用typename,也可以用class
- 1
- 2
两者没有什么不同。作为template的类型参数,意义完全相同。在使用习惯上来说,很多人喜欢使用typename,因为这暗示参数并非一定要是个class类型。
C++有时不会把class和typename等价。有时候一定要用typename;为了了解原理,先来谈谈在template内指涉(refer to)的两种名称。
用一个例子来说明,现在有个template function,接收STL兼容容器为参数,容器内对象可被赋值为int,这个函数打印第二个元素的值
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
现在解释一下两个local变量iterate和value。iterate类型为C::const_iterator,实际是什么取悦于template参数C。template内出现的名称如果依赖于某个template参数,称这个名称为从属名称(dependent names)。如果从属名称在class内呈嵌套状,称之为嵌套从属名称(nested dependent name)。C::const_iterator就是这样的嵌套从属名称(nested dependent name),且指涉某类型。value是int类型,不依赖template参数,称之为非从属名称(non-dependent names)。
嵌套从属名称有可能导致解析(parsing)困难,例如在print2dn这样做
template<typename C>
void print2nd(const C& container)
{
C::const_iterator* x;
……
}
表面上看是声明了一个local变量x,x类型是只需C::const_iterator的指针,这是因为我们直到C::const_iterator是个类型。如果不是这样呢?例如,C内有个static成员变量碰巧命名为const_iterator,或x是个global变量;这样的话就是一个相乘动作:const::const_iterator乘以x。编写C++解析器的人必须操心这样的输入。
在直到C是什么之前,不能确定C::cont_iterator是否为一个类型。当编译器解析template print2nd时,不知道C是什么东西。C++有个规则可以解析这个歧义状态:如果解析器在template中遭遇到一个嵌套从属名称,它便假设这个明白不是个类型,除非你告诉它是。所以缺省情况下,嵌套从属名称不是类型。这个规则还有个例外,稍后再提。
再来看看print2nd,C::const_iterator iter
只有在C::const_iterator
是个类型时才合理,但是C++缺省认为它不是。要矫正这个形式,我们必须告诉C++C::const_iterator
是个类型,在其前面放置关键字typename即可
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
“typename必须作为嵌套从属类型名称的前缀词”这一规则的例外是:typename不可以出现在base class list内的嵌套从属类型名称之前,也不可以在member initialization list(成员初值列)中作为base class修饰符。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
再来看一个typename例子。一个function template接受一个迭代器,我们打算为该迭代器指涉的对象做一份local复件(副本)temp
- 1
- 2
- 3
- 4
- 5
- 6
typename::std::iterator_traits<IterT>::value_type
是标准traits class(**条款**47)的一种运用。std::iterator_traits<IterT>::value_type
是个嵌套从属类名称,所以在其前面放置typename。如果使用std::iterator_traits<IterT>::value_type
感觉太长不习惯,可以考虑建立一个typedef,对于traits成员名称,普遍的习惯是设定typedef名称代表某个traits成员名称。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
最后提一下,typename相关规则在不同编译器上有不同的实践。这意味着typename和嵌套从属名称之间的互动,也许会在移植性方面带来头疼的问题。
总结
- 声明template参数时,前缀关键字class和typename可以互换。
- 使用关键字typename标识嵌套从属类型名称;但不得在base class list(基类列)或member initialization list(成员初值列)内以它作为base class修饰符。