解剖C++模板(1) —— 你从没仔细思考过的关键字

先说在前头,模板的初衷是泛型,但它不是固定格式,每一个关键字以及符号都有其规则。

这应该算是模板入门的经典模板头部了。 不接触模板元编程的 C++ 用户也很容易将它当作固定格式(像极了初学者觉得 cpp 文件开头一定要书写 using namespace std;),以至于在创建复杂模板或者多个模板配合使用时像无头苍蝇般写到哪儿算到哪儿,于是出现各种不明原因的错误。

这个系列目的在于说明这些规则,让 C++ 用户在写模板的时候知道自己在干嘛。

1、从尖括号说起

先说结论。尖括号里的内容是模板实例化的条件。它要求模板参数必须是类型(typename 的作用,后面会提到)。先看这张图。

千万别把它跟特化那一套规则混淆。这就是单纯的两个模板。函数名可以当作模板标识符, foo<int> 由于尖括号里的内容是类型,符合①的规则,因此这里必然将①实例化。而 foo<1> 尖括号里的内容是个整型,自然就是实例化②了。

也就是说,尖括号在这里是个条件约束符,用于规定满足某种条件时实例化该模板并调用。所以这段代码的报错就很好理解了:

尖括号里要求传入两个类型,而 foo<int> 只传入一个类型,不符合条件,触发 SFINAE 规则,于是继续匹配,直到找不到匹配模板而报错(这里了解 SFINAE 规则是什么)。

2、template 是什么

这个话题在这里还没法细讲。这里只能说,它有两种语境。当它后面跟随的符号为尖括号时,目的在于告诉编译期以下代码块无需实例化,等匹配。其二就是显示模板实例化的内容了,之后在模板特化的部分再细说。

在模板头部的 template 有点 if 的性质,尖括号中的内容如果不匹配,那么找其他同名模板进行匹配(就像 else if )。总之,到目前为止,template 关键字就是告诉模板"没调用到就别实例化"这个信息。

3、typename 是什么

这玩意目的也只是告诉编译器,接下来的内容是个类型。类比一下:

括号里面的 int 或者 char 无非在告诉编译器,它接收的是什么类型的参数。类型匹配则调用对应的函数(函数重载)。所以如果传入 foo 的参数无法隐式转换到 int 或者 char,比如 ostream 类型的参数,那么编译器会报错为找不到重载函数:

也就是说,当尖括号里的条件是 typename 时,必须传入类型才能满足条件,否则找不到匹配的模板(参照开篇第 3 张图)。

其次,这里的 typename 还有第二个作用。这也是之所以模板编程又叫泛型编程的原因。

T 在这里并不是实际存在的类型,而是一个类型形参。typename 的目的在于告诉编译器,这是个类型,不要报错。同样用作这个目的的 typename 可以参考这张图:

上面这张图一共出现两个 typename ( val_1 这行肯定报错,因为单独的 T::type 是个待决名。待决名的规则这里也先挖个坑 ),排除尖括号里的 typename 用作限制类型的目的之外,这两个 typename 作用完全一样,就是为了告诉编译器,这是个类型,不要报错。当然更多的细节要到偏特化才能说清楚。

以上算是先给模板开个头,后续将继续深入。

相关推荐
程序员爱德华4 小时前
Python与C++:异同点对比
c++·python
临床数据科学和人工智能兴趣组4 小时前
要使用 R Markdown,首先需要安装 R 和 RStudio,接着安装 rmarkdown 包
开发语言·数据挖掘·数据分析·r语言·r语言-4.2.1
Nebula嵌入式4 小时前
【C语言】09-深入解析main函数
linux·c语言·开发语言·嵌入式
神罚天下ET6 小时前
c# ACME client (补充)
开发语言·c#
小灰灰搞电子7 小时前
Qt 实现魔法导航菜单源码分享
开发语言·qt
天空'之城7 小时前
C 语言工业级通用组件手写 23:卡尔曼滤波(简易版)
c语言·卡尔曼滤波·嵌入式算法·工业级组件
持敬chijing7 小时前
Python概述
开发语言·python
888CC++7 小时前
C语言与C++的区别:从面向过程到面向对象
java·c语言·c++
Darkwanderor8 小时前
Linux系统编程实战项目:模拟实现shell
linux·c++
himobrinehacken9 小时前
揭秘Windows程序启动的神秘之旅
c++·安全