拥抱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进行声明。
- thinkcmf安装教程与目录结构详解 快速上手版
- finecms如何批量替换文章中的关键词?
- dedecms批量修改文章为待审核稿件怎么操作
- 群体智能算法-黏菌寻找食物最优路线行为模拟
- finecms设置伪静态后分享到微信不能访问怎么处理
- composer安装其实可以很简单 两行命令就解决了
- 章神的私房菜之数据预处理
- 如何调用finecms指定栏目的描述关键词
- finecms栏目文章页seo设置
- Logistic回归实战篇之预测病马死亡率(一)
- DedeCMS后台500错误一种原因是不支持PHP5.3、5.4及以上版本
- finecms指定从第几篇文章开始调用5条记录,并调用文章所在栏目
- finecms如何调用多个栏目的子栏目
- 从傅立叶变换到Gabor滤波器
- 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 数组属性和方法
- Python之turtle模块初体验
- tcsetpgrp failed重新编译tini
- s3cmd ls之迷惑
- 构建pyflink镜像
- apt-get update遇到NO_PUBKEY
- 遇到mpi worker exited on signal 9
- 容器共享GPU时查看容器使用的GPU编号
- oci runtime error: exec failed: container_linux.go:247: starting container process caused “exec: “/
- R|UpSet-集合可视化
- 美国队长的盾(一) 同心圆
- R|clusterProfiler-富集分析
- R|fastqcr QC数据处理
- R|timeROC-分析
- R|ML_code-线性回归(2)
- R|机器学习入门-多元线性回归(3)