C/C++编程:了解typename的双重意义

1059 篇文章 278 订阅

问题:在下面模板声明式中,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修饰符
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值