上篇文章c++11-17 模板核心知识(十四)—— 解析模板之依赖型模板名称 Dependent Names of Templates(.template/->template/::template) 介绍了依赖型模板名称,提到关于模板解析有六个大方面:

  • 非模板中的上下文相关性 Context Sensitivity in Nontemplates
  • 依赖型类型名称 Dependent Names of Types <-----
  • 依赖型模板名称 Dependent Names of Templates
  • using-declaration中的依赖型名称 Dependent Names in Using Declarations
  • ADL和显式模板实参 ADL and Explicit Template Arguments
  • 依赖性表达式 Dependent Expressions

这篇文章介绍下依赖型类型名称(Dependent Names of Types)。

模板名称的问题及解决

模板中的名称存在一个问题:它们有的时候不能被很好的分类,比如一个模板引用其他模板的名称,因为模板特化的存在,会让问题变得复杂一些。例如:

template <typename T> class Trap {
public:
enum { x }; // #1 x is not a type here
}; template <typename T> class Victim {
public:
int y;
void poof() {
Trap<T>::x *y; // #2 declaration or multiplication?
}
}; template <> class Trap<void> { // evil specialization!
public:
using x = int; // #3 x is a type here
}; void boom(Victim<void> &bomb) { bomb.poof(); }

如果你直接编译,会报错:

main.cc:30:14: error: unexpected type name 'x': expected expression
Trap<T>::x *y; // #2 declaration or multiplication?
^
main.cc:39:38: note: in instantiation of member function 'Victim<void>::poof' requested here
void boom(Victim<void> &bomb) { bomb.poof(); }
^
1 error generated.

这个问题和解决方案在c++11-17 模板核心知识(二)—— 类模板涉及过,这篇文章再展开说一下相关规则。

回到上面的例子,当编译器解析到#2处时,它需要决定Trap<T>::x是一个类型还是一个值,这决定了Trap<T>::x *y是声明一个指针还是做乘法。

问题是,在Trap中,Trap<T>::x是一个值,但是在全特化版本Trap<void>中,Trap<T>::x是一个类型。所以,这种情况实际是依赖模板参数T的,也就是依赖型类型名称(Dependent Names of Types)。

C++规定,只有当加上typename关键字后,依赖型类型名称才会被当做类型,否则会被当做一个值。这里typename的意义和声明一个模板时使用的typename是两个意思,所以不能用class来替换typename.

typename规则

当一个名称具备以下性质时,需要在名称前面加typename:

  • 是qualified name。
  • 不是Elaborated type specifier的一部分(例如,以class、struct、union、enum为开头的类型)
  • 名称不是用于指定基类继承的列表中,也不是位于引入构造函数的成员初始化列表中。
  • 依赖于模板参数。

例如:

template <typename(1) T>
struct S : typename(2) X<T>::Base {
S() : typename(3) X<T>::Base(typename(4) X<T>::Base(0)) {} typename(5) X<T> f() {
typename(6) X<T>::C *p; // declaration of pointer p
X<T>::D *q; // multiplication!
}
typename(7) X<int>::C *s; using Type = T;
using OtherType = typename(8) S<T>::Type;
};

下面逐一说下上面各个typename的使用场景(有的使用方式是错误的):

  • 第一个typename代表一个模板参数,不在此文章讨论范围内。
  • 第二和第三个typename是错误的使用方式,不需要添加,违反了上面的第3条规则。第二个出现在了指定基类继承的列表中,第三个出现在了构造函数的成员初始化列表。如果加上typename编译,会报如下错误:
main.cc:30:12: error: 'typename' is redundant; base classes are implicitly types
struct S : typename X<T>::Base {
^~~~~~~~~
  • 第四个typename是必须的,它满足上面第3条规则,且其他规则也满足。
  • 第五个typename是错误的,因为X不是一个qualified name,如果加上typename编译,会报:
main.cc:33:12: error: expected a qualified name after 'typename'
typename X<T> f() {
^
  • 第六个typename是必须的,上面讲过,代表一个类型。
  • 第七个typename是可有可无的,因为X<int>::C不依赖模板参数,即不是Dependent Name.
  • 第八个typename也是可有可无的,因为它指向的是current instantiation,这个概念下篇文章会讲到。

C++20 typename

是了,这一大堆乱七八糟的规则,谁也不想去记。C++20对typename的规则做了一些改善,有一些场景不再需要typename。详情大家可以参考 : The typename disambiguator for dependent names

(完)

朋友们可以关注下我的公众号,获得最及时的更新:

最新文章

  1. 使用CocoaPods过程中 Unable to find a specification for
  2. CSS实现三角形
  3. zw版【转发&#183;台湾nvp系列Delphi例程】HALCON Cast 使用方式
  4. 序列dp笔记√
  5. iOS Crash文件的解析
  6. 记微信开发(自定义回复&amp;关注回复)
  7. JQuery遍历json数组的3种方法
  8. 1、MySql的安装和连接测试并给root用户赋密码
  9. 11. 将博客部署到tomcat上
  10. PHP基础笔记
  11. [Reinforcement Learning] Model-Free Prediction
  12. PHP微信公众号JSAPI网页支付(下)
  13. github 遇到Permanently added the RSA host key for IP address &#39;192.30.252.128&#39; to the list of known hosts问题解决
  14. Linux内核的整体架构简介
  15. Java大数统计-hdu1316
  16. GsonUtil工具类
  17. 理解Path对路径进行操作的API
  18. nginx异常处理
  19. zabbix agent配置详解(windows)
  20. CentOS7安装sogou输入法

热门文章

  1. [MIT6.006] 11. Integer Arithmetic, Karatsuba Multiplication 整型算术,Karatsuba乘法
  2. KepServer与S7-1200PLC之间的OPC通信配置
  3. delete和truncate/drop恢复数据的过程
  4. 文本多行省略号(CSS最优方案)
  5. centos 7.6 虚拟机开启网卡
  6. deepin 安装最新版node
  7. vue在移动端使用alloyfinger手势库操作图片拖拽、缩放
  8. TCP的ACK机制
  9. 3dmax利用静止静态对象功能,制作精准击碎效果
  10. FL Studio时间面板讲解