很久没有看过C++模板,因为日常用不到。以前看书的东西已经忘记了。写此文方便自己抓住要点,快速开窍。
模板就是模板,它并不是某种的原始类型。如果把模板当成一个类型,在错误的概念下就会有很多奇怪的矛盾,不合理的地方。
恰如其名:编译器使用模板,通过更换模板参数来创建数据类型。这个过程就是模板实例化(Instantiation)。从模板类创建得到的类型称之为特例(specialization)。模板实例化取决于编译器能够找到可用代码来创建特例(称之为实例化要素,point of instantiation)。要创建特例,编译器不但要看到模板的声明,还要看到模板的定义。模板实例化过程是迟钝的,即只能用函数的定义来实现实例化。
理解了这一点,就基本理解了模板的本质。这也体现了2-level langurage的特点。这也是叫做:图灵完备的函数式编程的原因。
为了实例化,模板编译器很智能,必须能够推导,其核心就是推导出该有的类型。
2. 最初的确是类型泛型引入的,然后要支持3类模板参数:
1. type 2. non-type 2.template arg
然后要推导类型,就发现支持了分支与循环。差不多就图灵完备了。
举个例子:
#include
template<int N>
class sumt{
public: static const int ret = sumt<N-1>::ret + N;
};
template<>
class sumt<0>{
public: static const int ret = 0;
};
int main() {
std::cout << sumt<5>::ret << '\n';
std::cin.get(); return 0;
}
sumt<5>是一个实例化类型,
然后生成了sumt<4>,sumt<3>...sumt<0>
C++ 模板是图灵完备的,这使得 C++ 成为两层次语言(two-level languages,中文暂且这么翻译,文献[9]),其中,执行编译计算的代码称为静态代码(static code),执行运行期计算的代码称为动态代码(dynamic code),C++ 的静态代码由模板实现(预处理的宏也算是能进行部分静态计算吧,也就是能进行部分元编程,称为宏元编程,见 Boost 元编程库即 BCCL,文献[16]和文献[1] 10.4)。
具体来说 C++ 模板可以做以下事情:编译期数值计算、类型计算、代码计算(如循环展开),其中数值计算实际不太有意义,而类型计算和代码计算可以使得代码更加通用,更加易用,性能更好(也更难阅读,更难调试,有时也会有代码膨胀问题)。编译期计算在编译过程中的位置请见下图(取自文献[10]),可以看到关键是模板的机制在编译具体代码(模板实例)前执行:
从编程范型(programming paradigm)上来说,C++ 模板是函数式编程(functional programming),它的主要特点是:函数调用不产生任何副作用(没有可变的存储),用递归形式实现循环结构的功能。C++ 模板的特例化提供了条件判断能力,而模板递归嵌套提供了循环的能力,这两点使得其具有和普通语言一样通用的能力(图灵完备性)。
从编程形式来看,模板的“<>”中的模板参数相当于函数调用的输入参数,模板中的 typedef 或 static const 或 enum 定义函数返回值(类型或数值,数值仅支持整型,如果需要可以通过编码计算浮点数),代码计算是通过类型计算进而选择类型的函数实现的(C++ 属于静态类型语言,编译器对类型的操控能力很强)
参考: