拥抱STL -typename该怎么理解
1、缘起
不知道大家是不是从我的《走进STL - 空间配置器》一篇进来的,要看明白STL的代码,typename关键字是第一道坎,所以“空间配置器”写到一半,先来将这块石头为大家搬开。
2、耗神的示例:
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
- 1
是不是这句给整懵啦,说实话,我也整懵了,查了好久的资料。
3、typename是什么
typename的一个常见用法就是在模里担任泛型数据类型的申明关键字,如
template <typename T1,class T2>
,所以很多人对这个关键字就是:好熟啊,这啥呀,咋就想不起来了,哎这好像是那啥,嘶,那啥来着。。。因为模板一般也接触不多吧。
不过这里主要讲typename的另一个身份,对,另一个,身份。
有人是这么形容的:“内嵌 依赖 类型名”关键字。可能现在看着有点晕,且看我一个一个剖析。
内嵌:
首先这个内嵌并不是一定的。
对于typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
这种形式的(带上一个类(那个__type_traits是一个类)),才有内嵌的意义,意味着后面那个类型是属于这个类的。
依赖:
这里涉及到一个依赖名和非依赖名的概念,就像限定名被限定于名空间一样,依赖名是依赖于函数模板的名称,只有函数模板被实例化之后,依赖名才能以真面目示人。
千言万语不如来个栗子实在:
template <class T>
class test
{ int i; vector<int> vi; vector<int>::iterator vitr; T t; vector<T> vt; vector<T>::iterator viter;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在这个栗子中,i、vi、vitr就是有户口的“合法良民”,而t、vt、viter在模板T没有被实例化之前,并无法确定它们的真实身份,可以说是“没有户口”。
那么“没有户口”会有什么不良后果吗?会的,一个直接的潜在风险就是二义性,碧如说:
template <class T>
void test2()
{ T::iterator * iter; ...
}
- 1
- 2
- 3
- 4
- 5
- 6
这里要剑走偏锋了,看好咯:
如果这个模板被下面这个结构体实例化会有什么后果:
struct test3
{ static int iterator; ...
};
- 1
- 2
- 3
- 4
- 5
那这时候怎么办?
如果这个iterator刚好被初始化过,那将会是一个无效的乘法操作,我也不知道这是好运还是坏事。
如果这个iterator没有被初始化,那么将会报出"无效的XXX"这个错,我觉得直接报错倒是个好事儿。
所以,就需要typename的“类型名”特性大显身手了。
绕晕了没?马上带你兜回来。
类型名:
这世上心照不宣的事情很少的,更不要说跟你的编译器心照不宣了。不宣是可以的,有没有心照就不知道了。看C++标准:(已经给你翻译好了)
对于用于模板定义的依赖于模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前使用了typename关键字来修饰,编译器才会将该名称当成是类型。除了以上这两种情况,绝不会被当成是类型。
所以对于上面的栗子,你想让编译器自己知道T::iterator
是一个类型名,不出意外它是不知道的。
不过,你可以使用typename关键字进行修饰。
所以上面的栗子可以改成这样:
template <class T>
void test4() { typename T::iterator * iter; ...
}
- 1
- 2
- 3
- 4
- 5
4、总结
上面长篇大论讲了那么多,让我们回到最原始的地方再拨开迷雾见阳光:
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
- 1
has_trivial_destructor是依赖于模板的,内嵌于__type_traits 的一个类型。
5、其他使用规则
如果不讲一下typename的一些规则,总显得这篇文章少了点什么。
typename在下面情况下禁止使用:
模板定义之外,即typename只能用于模板的定义中
非限定类型,比如前面介绍过的int,vector之类
基类列表中,比如template class C1 : T::InnerType不能在T::InnerType前面加typename
构造函数的初始化列表中
如果类型是依赖于模板参数的限定名,那么在它之前必须加typename(除非是基类列表,或者在类的初始化成员列表中);
对于不会引起歧义的情况,仍然需要将typename加上。前面说了,歧义只是“不良后果”中的一种。
template <class T>
void test5() { typedef typename T::iterator iterator_type; ...
}
- 1
- 2
- 3
- 4
- 5
最后,建议在使用模板初始化时,多用typename替换class进行声明。
文章来源: lion-wu.blog.csdn.net,作者:看,未来,版权归原作者所有,如需转载,请联系作者。
原文链接:lion-wu.blog.csdn.net/article/details/105459176
- 点赞
- 收藏
- 关注作者
评论(0)