前言
template metaprogramming(TMP 模板元编程)是编写template-based C++程序并执行于编译期的过程。
所谓TMP,指的是以C++写成、执行于C++编译器内的程序。一旦TMP程序结束执行,其输出,也就是template具现出的若干C++源码,便会一如既往地编译。
TMP的优点
TMP有两大优点:
- 让某些事情变得容易,如果没有它,则可能无法完成某些任务。
- 由于TMP执行于编译期,所以可以将工作从运行期转移到编译期。这直接导致了运行期的错误可以提前到编译期。另外,程序的每一个方面效率都得到了提高(较小的执行文件、较短的运行期、较少的内存)。然而缺点是编译时间变得很长。
TMP实例
上一节所描述的traits解法就是TMP,它引发编译期发生于类型身上的if…else条件判断。traits-based TMP解法是针对不同类型执行不同代码,每个函数所使用的操作都确保可以实行于其类型。
TMP已被证明是图灵完备的,你可以使用它声明变量、执行循环、编写调用函数等等。但你写出来的东西肯定明显和正常的c++不同,我们之前那一张用TMP写出来的ifelse就是如此(重载),不过那毕竟是汇编语言层级的TMP。
为了大致地描述一下TMP的工作方式,我们首先看看循环操作。TMP没有循环构件,所以循环效果藉由递归完成。TMP主要是一个函数式语言,因此使用递归也十分自然。但是,TMP的递归也并非我们所熟知的递归,因为它并不涉及递归函数调用,而是涉及“递归模板具现化”。
TMP版阶乘计算
通过实现阶乘计算,我们来示范以“递归模板具现化”实现循环,以及创建使用变量。1
2
3
4
5
6
7
8template<unsigned n>
struct Factorial{
enum {value = n*Factorial<n-1>::value};
};
template<>
struct Factorial<0>{
enum{value =1};//递归基
}
首先,每一个Factorial template都是一个struct,value用来保存当前计算所得的阶乘。如果TMP有循环构件,value应该在每一次循环中更新,但实际上由于TMP系以“递归模板具现化”取代循环,每一个具现体有一份自己的value,每一个value有其循环内的适当值。
TMP可以达成的目标
- 确保量度单位正确。
在使用TMP时可以保证在编译期程序中所有量度单位的组合都正确,不论其计算多么复杂,这也就是为何TMP可被用来进行早期错误侦测。 - 优化矩阵运算。
比如五个矩阵的连乘会创建四个临时矩阵,甚至产生了四个作用于矩阵元素身上的循环。如果采用高级TMP技术,就能够削减内存消耗并合并循环,加速计算。 - 生成客户定制的设计模式。
将各种设计模式的某些特性加以整合,设计出独有的特性。这项技术已经超越了编程工艺领域如设计模式与智能指针,更广义地成为了generative programming的一个基础。
总结
- TMP可将工作由运行期移到编译期,因而得以较早完成错误侦测和高效运行。
- TMP可以定制设计模式,也可以规避某些并不适合特殊类型的代码。