Effective C++ — 条款42:了解typename的双重意义

了解typename的双重意义






提到一个问题:以下template声明式中,class和typename有什么不同? 

template<class T> class Widget;         //使用"class"

template<typename T> class Widget;      //使用"typename"

答案是没有不同! 当我们声明template类型参数的时候,class和typename的意义完全相同. 某些程序员始终比较喜欢class,因为可以

少打几个字.还 一些大佬喜欢typename,因为它暗示参数并非一定得是个class类型.少数开发人员在接受任何类型的时使用typename,

而在只接受用户自定义类型时 保留 旧式的class.然而从C++的角度来看,声明template参数时,不论使用关键字class或typename,意义完

全相同. 然而 C++ 并不总是把class 和 typename视为等价. 有时候你一定得使用typename.为了了解其时机,我们必须先谈谈你可以在

template内指(refer to) 两种名称. 看一下代码:

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

我在代码当中特别强调了两个local变量iter和value. iter的类型是C::const_iterator,实际是什么必须取决于template参数C,

tempalte内出现的 名称如果依赖于某个template参数,称之为从属名称.  如果从属名称在class内呈嵌套状,我们称它为嵌套

从属名词. C::const_iterator 就是这样的 一个名称. 实际上他还是一个嵌套从属类型名称,也就是个嵌套从属名称并且指涉某类型.

print2nd内的另一个local变量value,其类型是int。int是个并不倚赖任何template参数的名称.这样的名称称之为非从属名称. 叫它独

立名称也可以. 我们上面讲到的嵌套从属名称有可能导致解析困难.举个例子,假设我们令print2nd更愚蠢一点,这样起头:

template<typename C>
void print2nd(const C& container)
{
	C::const_iterator* x;
	...
}

看起来好像我们声明了一个x为一个local变量,它是一个指针,指向一个C::const_iterator. 但是我们之所以这样认为是因为我们"已

知道"啦 C::const_iterator是一个类型.如果C::const_iterator不是一个类型呢? 如果有一个static成员变量碰巧命名为

const_iterator,如果x碰巧 是一个global变量名称呢? 那样的话上述代码就不再是声明一个local变量,而是一个相乘动作. 虽然这在

们平时是看作不可能的,不过呢! C++ 解析器可不能容许偶然,他必须考虑到所有情况。

在我们知道C是什么之前,没有任何办法可以知道C::const_iterator是否为一个类型.而当编译器开始解析template print2nd时,尚未

知道C是一 个什么东西. C++有个规则可以解析此一歧义状态:如果解析器在template中遭遇一个嵌套从属类型,他便假设这名称不是一个

类型,除非你告诉他是。 所以缺省情况下嵌套从属名称不是一个类型. 那你怎么告诉它就是一个类型呢??  

很简单使用我们的typename. 看下面例子:

template<typename C>
void prnt2nd(const C& container,C::iterator iter) 
{
	if (container.size() >= 2)
	{}
}


void main()
{
	list<int> q;
	list<int>::iterator it;
	prnt2nd<list<int>>(q,it);
 	system("pause");
 }

我们只想验证一下嵌套从属结构是否能逃得过C++编译器的法眼? 我们可以看到它直接在参数当中使用了C::iterator.这里就是一个嵌套

从属结构. 我们程序运行,可以发现是会编译出错额. 所以你要让编译器知道他就是一个类型,所以呢,在C::iterator前面加上typename

告诉编译器我是一个类 型.这样编译器就不会帮你找事情了. 这样修改 程序就可以跑过了.

#include<iostream>
#include<list>

using namespace std;


template<typename C>
void prnt2nd(const C& container,typename C::iterator iter) 
{
	if (container.size() >= 2)
	{}
}


void main()
{
	list<int> q;
	list<int>::iterator it;
	prnt2nd<list<int>>(q,it);
 	system("pause");
 }


上述的C并不是嵌套从属类型名称,所以声明container不需要以typename为前导,但是C::iterator是一个从属类型名称. 所以must以

typename为 前导. 当然typename也会有特殊情况. "typename必须作为嵌套从属类型名称的前缀词"适用于大多场景,BUT typename不可

以出现在base classes list 内的嵌套从属类型名称之前,也不可以在member initalization list(成员初始化列表)中作为

base class 修饰符. 代码举例:

//伪代码
template<typename T>
class Derived : public Base<T>::Nested { //base class list 中不允许使用"typename"
public:								 
	explicit Derived(int x)
		:Base<T>::Nested(x)            //可变参数列表中,不允许使用"typename"
	{
			typename Base<T>::Nested temp; //这里就需要使用到"typename"
	}
};

这样有时候真的你万一没记起来,也挺烦人的不过呢 ? 一旦你有一点经验也就能勉勉强强接受它.

最后的最后,typename相关规则在不同的编译器上有不同的实践. 某些编译器接受的代码原本该有typename却遗漏了.原本不该有

typename却出现了 还有少数的编译器根本就拒绝typename。这意味着typename和"嵌套从属类型名称"之间的互动,也许会在移植性方面

给你带来一点小麻烦.

最后总结:

1.声明template参数时,前缀关键字class和typename可互换.

2.请使用关键字typename标识嵌套从属类型名称; 但是不得在base class lists(基类列)或者member initialization list(初始化成

员列表)内 以它作为base class修饰符.



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值