解剖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 作用完全一样,就是为了告诉编译器,这是个类型,不要报错。当然更多的细节要到偏特化才能说清楚。

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

相关推荐
从零开始的代码生活_1 小时前
C++ 内存管理:从内存分区到 new/delete 底层原理
开发语言·c++·后端
wabs6661 小时前
关于单调栈【力扣739.每日温度的思考】
java·开发语言
AOwhisky1 小时前
Python 学习笔记(第三期)——流程控制核心知识点自测与详解
开发语言·笔记·python·学习·云原生·运维开发·流程控制
aaPIXa6222 小时前
C++ 用 13 条规则让模型写出安全现代 C++
java·c++·安全
逆向编程2 小时前
Socket异常连接自动清理方案
开发语言
枕星而眠3 小时前
【数据结构】红黑树入门指南
运维·数据结构·c++·后端
2601_949817723 小时前
C++指针与引用深度精讲:底层原理、差异对比与高阶实战陷阱
java·jvm·c++
va学弟3 小时前
Java 网络通信编程(10):Channel 和 Selector
java·开发语言
爱吃提升3 小时前
MATLAB impulse函数脉冲响应完整实操教程
开发语言·matlab
小饕4 小时前
从 1080 Ti 到树莓派 4:Qwen2.5-0.5B Function Calling 端侧部署七步实战
开发语言·人工智能