有时间的建议先看下上篇文章 : c++11-17 模板核心知识(十三)—— 名称查找与ADL

tokenization与parsing

绝大多数语言在编译的时候都有两个阶段:

  • tokenization,或者叫scanning/lexing
  • parsing

tokenization阶段会读取源码并生成一系列token. 例如:int *p = 0;,tokenizer会生成关键字int、运算符*、标识符p、运算符=、整数0、运算符;

接下来,parser会递归的减少标记,寻找已知的模式。例如:token 0是一个合法的表达式,*p组合也是一个合法的声明,它和后面的=0组合也是一个合法初始化声明。最后,int是一个已知的类型,后面跟着初始化声明 : *p=0,所以,我们得到了一个初始化p的声明

解析模板之类型的依赖名称 Dependent Names of Templates

关于模板解析有六个大方面:

  • 非模板中的上下文相关性 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 Templates)

这里有一个很重要的概念 :c++11-17 模板核心知识(十三)—— 名称查找与ADL中介绍过的Dependent Name:依赖于模板参数的名称,也就是访问运算符左面的表达式类型依赖于模板参数。例如:std::vector::iterator是一个 Dependent Name,但假如T是一个已知类型的别名(using T = int),那就不是Dependent Name。

通常而言, 编译器会把模板名称后面的<当做模板参数列表的开始,否则,<就是比较运算符。但是,当引用的模板名称是Dependent Name时,编译器不会假定它是一个模板名称,除非显示的使用template关键字来指明,模板代码中常见的->template.template::template就应用于这种场景中。

下面看几个例子。

Example One

template<unsigned long N>
void printBitset (std::bitset<N> const& bs) {
std::cout << bs.template to_string<char, std::char_traits<char>, std::allocator<char>>();
}

这里,参数bs依赖于模板参数N。所以,我们必须通过template关键字让编译器知道bs是一个模板名称,否则按照上面的规则,<会被当做比较符——小于号。

Example Two

The template keyword as qualifier (C++ only)中的例子:

#include <iostream>
using namespace std; class X {
public:
template <int j> struct S {
void h() {
cout << "member template's member function: " << j << endl;
}
};
template <int i> void f() {
cout << "Primary: " << i << endl;
}
}; template<> void X::f<20>() {
cout << "Specialized, non-type argument = 20" << endl;
} template<class T> void g(T* p) {
p->template f<100>();
p->template f<20>();
typename T::template S<40> s; // use of scope operator on a member template
s.h();
} int main()
{
X temp;
g(&temp);
}

这里,参数p依赖模板参数T。注意typename T::template S<40> s;的使用。

Example Three

template <typename T> class Shell {
public:
template <int N> class In {
public:
template <int M> class Deep {
public:
virtual void f();
};
};
}; template <typename T, int N> class Weird {
public:
void case1(typename Shell<T>::template In<N>::template Deep<N> *p) {
p->template Deep<N>::f(); // inhibit virtual call
} void case2(typename Shell<T>::template In<N>::template Deep<N> &p) {
p.template Deep<N>::f(); // inhibit virtual call
}
};

参数p依赖模板参数T。编译器不会去判断p.Deep是不是模板。如果不指定template,那么p.Deep<N>::f()就会被解析成((p.Deep)<N)>f();<被当做比较符。

基于上面的例子,我们也可以知道,->template.template::template只存在于模板中,并且是在Dependent Name的场景下使用(依赖于模板参数)。

(完)

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

c++11-17 模板核心知识(十四)—— 解析模板之依赖型模板名称(.template/->template/::template)的更多相关文章

  1. c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters

    概念 举例 模板的模板参数的参数匹配 Template Template Argument Matching 解决办法一 解决办法二 概念 一个模板的参数是模板类型. 举例 在c++11-17 模板核 ...

  2. c++11-17 模板核心知识(十五)—— 解析模板之依赖型类型名称与typename Dependent Names of Types

    模板名称的问题及解决 typename规则 C++20 typename 上篇文章c++11-17 模板核心知识(十四)-- 解析模板之依赖型模板名称 Dependent Names of Templ ...

  3. c++11-17 模板核心知识(十一)—— 编写泛型库需要的基本技术

    Callables 函数对象 Function Objects 处理成员函数及额外的参数 std::invoke<>() 统一包装 泛型库的其他基本技术 Type Traits std:: ...

  4. c++11-17 模板核心知识(二)—— 类模板

    类模板声明.实现与使用 Class Instantiation 使用类模板的部分成员函数 Concept 友元 方式一 方式二 类模板的全特化 类模板的偏特化 多模板参数的偏特化 默认模板参数 Typ ...

  5. c++11-17 模板核心知识(一)—— 函数模板

    1.1 定义函数模板 1.2 使用函数模板 1.3 两阶段翻译 Two-Phase Translation 1.3.1 模板的编译和链接问题 1.4 多模板参数 1.4.1 引入额外模板参数作为返回值 ...

  6. c++11-17 模板核心知识(八)—— enable_if<>与SFINAE

    引子 使用enable_if<>禁用模板 enable_if<>实例 使用Concepts简化enable_if<> SFINAE (Substitution Fa ...

  7. c++11-17 模板核心知识(九)—— 理解decltype与decltype(auto)

    decltype介绍 为什么需要decltype decltype(auto) 注意(entity) 与模板参数推导和auto推导一样,decltype的结果大多数情况下是正常的,但是也有少部分情况是 ...

  8. c++11-17 模板核心知识(十三)—— 名称查找与ADL

    名称分类 名称查找 ordinary lookup ADL (Argument-Dependent Lookup) 官网的例子 ADL的缺点 在C++中,如果编译器遇到一个名称,它会寻找这个名称代表什 ...

  9. How Javascript works (Javascript工作原理) (十四) 解析,语法抽象树及最小化解析时间的 5 条小技巧

    个人总结:读完这篇文章需要15分钟,文章介绍了抽象语法树与js引擎解析这些语法树的过程,提到了懒解析——即转换为AST的过程中不直接进入函数体解析,当这个函数体需要执行的时候才进行相应转换.(因为有的 ...

随机推荐

  1. SpringBoot中BeanValidation数据校验与优雅处理详解

    目录 本篇要点 后端参数校验的必要性 不使用Validator的参数处理逻辑 Validator框架提供的便利 SpringBoot自动配置ValidationAutoConfiguration Va ...

  2. Serilog 源码解析——数据的保存(上)

    在上一篇中,我们主要研究了Serilog是如何解析字符串模板的,它只是单独对字符串模板的处理,对于日志记录时所附带的数据没有做任何的操作.在本篇中,我们着重研究日志数据的存储方式.(系列目录) 本篇所 ...

  3. 企业网络拓扑VRRP主备功能实例(一)

    组网图形  VRRP主备备份简介 通常,同一网段内的所有主机上都存在一条相同的.以网关为下一跳的缺省路由.主机发往其他网段的报文将通过缺省路由发往网关,再由网关进行转发,从而实现主机与外部网络的通信. ...

  4. 重要 | Spark分区并行度决定机制

    最近经常有小伙伴在本公众号留言,核心问题都比较类似,就是虽然接触Spark有一段时间了,但是搞不明白一个问题,为什么我从HDFS上加载不同的文件时,打印的分区数不一样,并且好像spark.defaul ...

  5. ubuntu13.04修改默认启动内核

    ubuntu下面的启动内核选项跟其他操作系统不一样,有个子菜单,比如我在默认的ubuntu13.04上安装了一个新的内核3.14.5,那么默认的第一项是3.14.5内核,第二项是一个子菜单,第二项里面 ...

  6. 对App应用架构搭建的一些思考

    当下随着App开发技术的越来越成熟,多人协同开发必不可少,一个团队中每个人的代码风格.技术栈都存在差异,因此统一一套成熟的开发架构必不可少,可以提高开发效率.统一代码风格.为项目维护提供便利. 当下A ...

  7. 分布式监控系统之Zabbix宏、模板和自定义item

    前文我们聊了下zabbix的基础使用,包括主机的添加.监控项.触发器.action以及告警通知的配置,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/140073 ...

  8. Python 单表查询数据

    import pymssql #引入pymssql模块# -----------------------------------------------------import pymssql #引入 ...

  9. php批量转换时间戳

    //批量转换时间戳 array_map(array($this, 'myfunction'),'时间戳数组'); //如 array('time1'=>11,'time2'=>2222); ...

  10. Linux学习 - 02 使用 - Centos8 - 『更换rpm/epel包源为国内源』

    1. Centos8 - 『更换rpm/epel包源为国内源』 centos 8 默认是会读取centos.org的mirrorlist的,所以一般来说是不需要配置镜像的. 如果你的网络访问mirro ...