拥抱STL -typename该怎么理解

举报
看,未来 发表于 2020/12/29 23:46:39 2020/12/29
【摘要】 文章目录 1、缘起2、耗神的示例:3、typename是什么内嵌:依赖:类型名: 4、总结5、其他使用规则 1、缘起 不知道大家是不是从我的《走进STL - 空间配置器》一篇进来的,要看明白STL的代码,typename关键字是第一道坎,所以“空间配置器”写到一半,先来将这块石头为大家搬开。 2、耗神的示例: typedef typen...

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

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。