拥抱STL -typename该怎么理解
1、缘起
不知道大家是不是从我的《走进STL - 空间配置器》一篇进来的,要看明白STL的代码,typename关键字是第一道坎,所以“空间配置器”写到一半,先来将这块石头为大家搬开。
2、耗神的示例:
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
是不是这句给整懵啦,说实话,我也整懵了,查了好久的资料。
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;
};
在这个栗子中,i、vi、vitr就是有户口的“合法良民”,而t、vt、viter在模板T没有被实例化之前,并无法确定它们的真实身份,可以说是“没有户口”。
那么“没有户口”会有什么不良后果吗?会的,一个直接的潜在风险就是二义性,碧如说:
template <class T>
void test2()
{
T::iterator * iter;
...
}
这里要剑走偏锋了,看好咯: 如果这个模板被下面这个结构体实例化会有什么后果:
struct test3
{
static int iterator;
...
};
那这时候怎么办? 如果这个iterator刚好被初始化过,那将会是一个无效的乘法操作,我也不知道这是好运还是坏事。 如果这个iterator没有被初始化,那么将会报出"无效的XXX"这个错,我觉得直接报错倒是个好事儿。
所以,就需要typename的“类型名”特性大显身手了。
绕晕了没?马上带你兜回来。
类型名:
这世上心照不宣的事情很少的,更不要说跟你的编译器心照不宣了。不宣是可以的,有没有心照就不知道了。看C++标准:(已经给你翻译好了)
对于用于模板定义的依赖于模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前使用了typename关键字来修饰,编译器才会将该名称当成是类型。除了以上这两种情况,绝不会被当成是类型。
所以对于上面的栗子,你想让编译器自己知道T::iterator
是一个类型名,不出意外它是不知道的。
不过,你可以使用typename关键字进行修饰。
所以上面的栗子可以改成这样:
template <class T>
void test4() {
typename T::iterator * iter;
...
}
4、总结
上面长篇大论讲了那么多,让我们回到最原始的地方再拨开迷雾见阳光:
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
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;
...
}
最后,建议在使用模板初始化时,多用typename替换class进行声明。
- Spring Cloud中如何保证各个微服务之间调用的安全性
- Spring Boot Actuator监控页面报错解决
- Spring Cloud中如何优雅的使用Feign调用接口
- Spring Cloud Eureka 集群高可用
- Spring Cloud Eureka 增加权限认证
- Spring Cloud Eureka 初探
- 房价网是怎么使用分布式作业框架elastic-job
- Spring Cloud Sleuth Zipkin 展示追踪数据
- Spring cloud Zuul Filter 使用小经验
- Spring Cloud Eureka REST 接口
- Spring Cloud Eureka 控制台快速查看Swagger API文档
- Spring Cloud Feign 启动UnsatisfiedDependencyException
- Spring Cloud Zuul结合Smconf配置中心动态进行IP黑名单限制
- 高性能NIO框架Netty入门篇
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Flutter基础widgets教程-TabBarView篇
- python + selenium 爬虫模拟登录破解无原图滑动验证码
- python 轻量级定时框架apscheduler,周中定时给自己发送邮件。
- python 舆情分析 nlp主题分析 (1) 待续
- Flutter基础widgets教程-Text篇
- python 舆情分析 nlp主题分析 (2)-结合snownlp与jieba库,提高分词与情感判断 待续
- No qualifying bean of type 'com.pjh.service.Imp.serviceImp' available和Exception in thread "main" jav
- python音频文件中pcm格式提取
- Spring系列之事务的控制 注解实现+xml实现+事务的隔离等级
- Leetcode刷题 237. 删除链表中的节点 两行代码实现
- python提取视频第一帧图片
- Leetcode刷题 206. 反转链表 递归迭代两种方法实现
- airtest本地连接和远程连接
- Flutter基础widgets教程-TextField篇
- poco对象生成的几种方式根据你使用不同的ui决定