考虑以下代码:
template<class T> class Widget;
template<typename T> class Widget;
class和typename有什么不同?
答案:没有不同.当我们声明template类型参数,class和typename的意义完全相同。
然而C++并不总是把class和typename视为等价.有时候一定得使用typename.
假设有以下代码:
有一个函数模板,接受一个STL容器为参数,这个函数仅仅只是打印其第二个元素值.
template<typename C>
void print2nd(const C & container)
{
if(container.size()>= 2)
{
c::const_iterator iter(container.begin());
++iter;
int value = *iter;
std::cout << value;
}
}
iter
的类型是c::const_iterator
,实际是什么取决于参数C,模板内出现的名称如果相依于某个模板参数,称之为从属名称.如果从属名称在class内呈嵌套状,我们称它为嵌套从属名称.
c::const_iterator
就是一个嵌套从属名称。
value的类型是int,int并不依赖任何模板参数的名称,这样的名称是谓非从属名称.
嵌套从属名称有可能导致解析困难,看下面的例子:
template<typename C>
void print2nd(const C & container)
{
C::const_iterator *x;
}
看起来好像我们声明x为一个局部变量,它是一个指针,指向一个C::const_iterator
.但它之所以被这样认为,是因为我们已经知道C::const_iterator
是个类型.如果C::const_iterator
不是个类型呢?如果C有个static成员变量而碰巧被命名为const_iterator,或者如果x碰巧是一个全局变量名称呢?
这样的话,上面的代码不再是声明一个局部变量了,而是一个相乘动作:C::const_iterator乘以x
.
在我们知道C是什么之前,没有任何办法知道C::const_iterator
是否为一个类型.而当编译器开始解析模板print2nd时,尚未知道C是什么东西.
C++有个规则解析这一歧义状态:如果解析器在模板中遭遇一个嵌套从属名称,它便假设这名称不是一个类型,除非你告诉它是.
所以上面的代码不是有效的代码,iter声明式只有在C::const_iterator
是个类型时才合理,但我们并没有告诉C++说它是,要想告诉C++说C::const_iterator
是个类型,需要在前面加关键字typename
:
typename c::const_iterator iter(container.begin());
一般性规则很简单:任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在紧临它的前一个位置放上关键字typename.
这个规则的例外是:
typename不可用出现在基类列表内的嵌套从属类型名称之前,也不能在成员初始化列表中作为基类的修饰符.比如:
template<typename T>
class Derived:public Base<T>::Nested //不允许typename
{
public:
explicit Derived(int x) :Base<T>::Nested(x) //不允许typename
{
typename Bast<T>::Nested temp;//需要typename
...
}
};
…
总结:
1.声明template参数时,class和typename可互换.
2.使用关键字typename标识嵌套从属类型名称,但是不能在基类列或成员初始化列表中以它作为基类修饰符