前言

在C++模板函数的使用过程中,我们经常可以看到一个typename的使用,例如这样的操作

但是除此之外,我们也会经常看到这样的用法

那么这里就要问大家,这C++类似的用法下有什么区别呢,且听我细细道来。


作者:良知犹存

转载授权以及围观:欢迎添加微信公众号:羽林君


由来分析

   "typename"是一个C++程序设计语言中的关键字。当用于泛型编程时是另一术语"class"的同义词。这个关键字用于指出模板声明(或定义)中的非独立名称(dependent names)是类型名,而非变量名。

我们经常会这么用 typename,这是一项C++编程语言的泛型编程(或曰“模板编程”)的功能,typename关键字用于引入一个模板参数。

template <typename T>
const T& max(const T& x, const T& y)
{
if (y < x) {
return x;
}
return y;
}

在模板定义语法中关键字 class 与 typename 的作用完全一样

template<class T>
const T& max(const T& x, const T& y)
{
if (y < x) {
return x;
}
return y;
}

这里 class 关键字表明T是一个类型,后来为了避免 class 在这两个地方的使用可能给人带来混淆,所以引入了 typename 这个关键字,它的作用同 class 一样表明后面的符号为一个类型。

那class使用就够了,为什么又引入了新的关键词 typename ,关于这个问题,Stan Lippman 曾在其博客中表示,最早 Stroustrup 使用 class 来声明模板参数列表中的类型是为了避免增加不必要的关键字;后来委员会认为这样混用可能造成概念上的混淆才加上了 typename 关键字。

而使用 typename 的作用就是告诉 c++ 编译器,typename 后面的字符串为一个类型名称,而不是成员函数或者成员变量,这个时候如果前面没有 typename,编译器没有任何办法知道 T::LengthType 是一个类型还是一个成员名称(静态数据成员或者静态函数),所以编译不能够通过。

问题浮现

那么问题来了,什么情况下,class定义之后,编译不能通过呢?

template<typename T>
void fun(const T& proto){         T::const_iterator it(proto.begin());
}

发生编译错误是因为编译器不知道T::const_iterator是个类型。万一它是个变量呢? T::const_iterator的解析有着逻辑上的矛盾: 直到确定了T是什么东西,编译器才会知道T::const_iterator是不是一个类型; 然而当模板被解析时,T还是不确定的。这时我们声明它为一个类型才能通过编译:

而且在模板实例化之前,完全没有办法来区分它们,这绝对是滋生各种bug的温床。这时C++标准委员会再也忍不住了,与其到实例化时才能知道到底选择哪种方式来解释以上代码,委员会决定引入一个新的关键字,这就是typename

千呼万唤始出来,我们来看看C++标准:

对于用于模板定义的依赖于模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前使用了 typename 关键字来修饰,编译器才会将该名称当成是类型。除了以上这两种情况,绝不会被当成是类型。

因此,如果你想直接告诉编译器 T::const_iterator 是类型而不是变量,只需用 typename修饰:

typename    T::const_iterator it(proto.begin());

这样编译器就可以确定T::const_iterator是一个类型,而不再需要等到实例化时期才能确定,因此消除了前面提到的歧义。

嵌套从属类型

事实上类型T::const_iterator依赖于模板参数T, 模板中依赖于模板参数的名称称为从属名称(dependent name), 当一个从属名称嵌套在一个类里面时,称为嵌套从属名称(nested dependent name)。 其实T::const_iterator还是一个嵌套从属类型名称(nested dependent type name)。

嵌套从属名称是需要用typename声明的,其他的名称是不可以用typename声明的。比如下面是一个合法的声明:

template<typename T>
void fun(const T& proto ,typename  T::const_iterator it);

使用

在定义类模板或者函数模板时,typename 和 class 关键字都可以用于指定模板参数中的类型。也就是说,以下两种用法是完全等价的。

template<typename T> /* ... */;
template<class T> /* ... */;

既然typename关键字已经存在,而且它也可以用于最常见的指定模板参数,那么为什么不废除class这一用法呢?答案其实也很明显,因为在最终的标准出来之前,所有已存在的书、文章、教学、代码中都是使用的是class,可以想像,如果标准不再支持class,会出现什么情况。

使用关键字typename代替关键字class指定模板类型形参更为直观,毕竟,可以使用内置类型(非类类型)作为实际的类型形参,而且,typename更清楚地指明后面的名字是一个类型名。但是,关键字typename是作为标准C++的组成部分加入到C++中的,因此旧的程序更有可能只用关键字class

这就是我分享的c++的typename,此外如果大家有什么更好的思路,也欢迎分享交流哈。

END

推荐阅读

【1】linux开发各种I/O操作简析,以及select、poll、epoll机制的对比

【2】嵌入式底层开发的软件框架简述

【3】CPU中的程序是怎么运行起来的 必读
【4】什么?还不懂c++vector的用法,你凭什么勇气来的!
【5】阶段性文章总结分析

本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。

参考链接:

https://liam.page/2018/03/16/keywords-typename-and-class-in-Cxx/

https://harttle.land/2015/09/09/effective-cpp-42.html

http://feihu.me/blog/2014/the-origin-and-usage-of-typename/

更多分享,扫码关注我

C++typename的由来和用法的更多相关文章

  1. 转载:C++ typename的起源与用法

    转载:http://feihu.me/blog/2014/the-origin-and-usage-of-typename/#typename 侯捷在Effective C++的中文版译序中提到: C ...

  2. <转>详解C++的模板中typename关键字的用法

    用处1, 用在模板定义里, 标明其后的模板参数是类型参数. 例如: template<typename T, typename Y> T foo(const T& t, const ...

  3. typename在C++中的用法

    . //在C++中typename一般用来声明模板的模板参数(template parameter): template<typename T> class X; //T是一个模板参数 . ...

  4. C++ 中 typename

    声明template参数时, 前缀关键字class和typename可以互换; 使用关键字typename标识嵌套从属类型名称, 但不需在基类列表和成员初始化列表内使用. 从属名称(dependent ...

  5. [c++] Templates

    Template是编译时多态.所有的模板都是在编译时产生对应的代码,它没有面向对象中的虚表,无法实现动态多态. Function Template A function template is a p ...

  6. C++资料收集&整理

    类别 地址 ZLib c++ zlib(qt)压缩与解压缩 STL STL学习之map容器(2)_insert Basic std::nothrow new (nothrow) T() 的原理 C++ ...

  7. C# 中的 null 包容运算符 “!” —— 概念、由来、用法和注意事项

    在 2020 年的最后一天,博客园发起了一个开源项目:基于 .NET 的博客引擎 fluss,我抽空把源码下载下来看了下,发现在属性的定义中,有很多地方都用到了 null!,如下图所示: 这是什么用法 ...

  8. C++中typename关键字的用法

    我在我的 薛途的博客 上发表了新的文章,欢迎各位批评指正. C++中typename关键字的用法

  9. [Z] 关于c++ typename的另一种用法

    在看c++ primer的时候见到了一下这种用法: typedef typename std::vector<int>::size_type size_type; 觉得这里面的typena ...

随机推荐

  1. Liunx运维(十)-网络管理命令

    文档目录: 一.ifconfig:配置或显示网络接口信息 二.ifup:激活网络接口 三.ifdown:禁用网络接口 四.route:显示或管理理由表 五.arp:管理系统的arp缓存 六.ip:网络 ...

  2. 记一次Goroutine与wg导致的问题

    前言 今天发现了一个问题是之前一直没有注意到的,这里记一下 正文 Send Closed Chan 问题概述 代码逻辑是启动时启动多个 channel, channel1 获取数据监听数据处理后发送给 ...

  3. jenkins + Ansible Plugin + ansi-color 让结果显示颜色

    1 安装jenkins: 此处省略百余字......   2 安装jenkins的插件: Ansible Plugin AnsiColor Plugin    3 设置job 内容 让ansible ...

  4. SAP中的密码输入框

    在SAP中的密码输入框,可分为两种情况: 1.用selection语句书写的选择屏幕上的密码输入框 实现的方式就是在AT SELECTION-SCREEN OUTPUT事件中写入如下代码: LOOP ...

  5. Ice系列--强大如我IceGrid

    前言 IceGrid是一个提供服务定位和服务激活的组件,但它的功能远不止于此.从它的命名可以看出它的设计理念-网格计算(grid computing).网格计算被定义为由一系列关联的廉价计算机组成的计 ...

  6. 响应式编程库RxJava初探

    引子 在读 Hystrix 源码时,发现一些奇特的写法.稍作搜索,知道使用了最新流行的响应式编程库RxJava.那么响应式编程究竟是怎样的呢? 本文对响应式编程及 RxJava 库作一个初步的探索. ...

  7. Docker 如何动态修改容器端口映射

    Docker端口映射往往是Docker Run命令时通过-p将容器内部端口映射到宿主机的指定端口上,一般来说容器的端口所对应的端口是提前确定需要映射的.但是有些情况下不得不需要临时映射端口,例如Doc ...

  8. 安装jdk-windows系统

    1. 明确需要安装的jdk版本,注意jdk存在小版本号,例如jdk1.7_51,如果不清楚小版本号的话建议安装最新版本的jdk: 2. 打开cmd命令窗口,输入java -version查看本机是否安 ...

  9. Python 中 lru_cache 的使用和实现

    在计算机软件领域,缓存(Cache)指的是将部分数据存储在内存中,以便下次能够更快地访问这些数据,这也是一个典型的用空间换时间的例子.一般用于缓存的内存空间是固定的,当有更多的数据需要缓存的时候,需要 ...

  10. 原生ajax分享

    最近被大佬问了一个很有趣的问题,你还能手打出一个ajax吗?,我当时的想法是有现成的为什么要自己打,后来我反思了一下(只有靠自己才是强者),在这里给大家分享一个我自己打的ajax,也是自己的一个知识点 ...