typename的双重意义

typename的双重意义

当我们在定义类模板(或函数模板)时,通常会用下面两种方式来表示,那么class和typename有什么不同呢?

template<class T> class widget;		// class
template<typename T> class widget;	// typename

答案:没有不同,当我们声明template类型参数,不论使用关键字class或typename,意义完全相同

然而在C++中class和 typename并不总是等价的。有时候一定得使用typename

假设我们有个函数模板,传入STL容器作为参数,容器内的对象可被赋值为int。这个函数的功能仅仅只是打印其第二个元素值。

template<typename C>
void print2nd (const C& container)    	//打印容器内的第二元素
{										
	if (container.size() >= 2)
    {
		C::const_iterator iter (container.begin());	//取得第一元素的迭代器
        ++iter;										//将iter移往第二元素
		int value = *iter;							//将该元素复制到某个int.
		std::cout << value;							//打印那个int.
	}
}
int main() 
{
	std::vector<int> v = {1, 2, 3, 4};
    print2nd(v);
	return 0;
}

看上去这份代码并没有什么问题,但编译会报错:

报错的原因是:我们认为iter的类型是C::const_iterator,实际是什么类型取决于template参数C。但这里编译器并不能确定是一个类型,还是类中的静态成员变量,于是出现了报错

这样可能并不好理解,我们来举个简单的例子:

template<typename C>
void print2nd(const C& container)
{
    C::const_iterator * x;
    ...
}
class Bar 
{
	const static int const_iterator = 10;
};

C::const_iterator * x;这行代码看起来像是声明x为一个局部变量,它是个指针,指向一个C::const_iterator类型。但它之所以被这样认为,是因为我们默认C::const_iterator是个类型。

如果C::const_iterator不是某个类型呢?如果C有个static成员变量而碰巧被命名为const_iterator,或x碰巧是个全局变量名称呢?那样的话上述代码就不再是声明一个局部变量,而是一个相乘动作:C::const_iterator乘以x

template内出现的名称如果相依于某个template参数,称之为从属名称(dependent names)。如果从属名称在 class内呈嵌套状,我们称它为嵌套从属名称(nested dependent name)。C::const_iterator就是这样一个名称。实际上它还是个嵌套从属类型名称(nesteddependent typename),也就是个嵌套从属名称并且指涉某类型。

现在应该很清楚为什么会报错了吧。iter声明只有在C::const_iterator是个类型时才合理,但我们并没有告诉C++说它是,于是C++假设它不是。若要改变这种情况,我们必须告诉C++说C::const_iterator是个类型。只要在它之前放置关键字typename即可:

template<typename C>
void print2nd (const C& container)//这是合法的C++代码
{
    if (container.size() >=2)
    {
		typename C::const_iterator iter (container.begin());
        ...
	}
    ...
}

一般性规则很简单:任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在紧临它的前一个位置放上关键字typename。

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
    ...
}
...
};

注意

  • 声明template参数时,前缀关键字class和 typename可互换。
  • 使用关键字typename标识嵌套从属类型名称;
  • 基类列表初始化列表不使用typename。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值