条款48: 认识 template 元编程
Be aware of template metaprogramming .Template metaprogramming(TMP,模板元编程)是编写 template-based C++程序并执行于编译期的过程.所谓 template metaprogram(模板元程序)是以C++写成,执行于C++编译器内的程序.一旦TMP程序结束执行,其输出,也就是从 template 具现出来的若干C++源码,便会一如往常地被编译.
TMP有两个伟大的效力. 第一,它让某些事情更容易.第二,由于 template metaprogram执行于C++编译期,因此可将工作从运行期转移到编译期.这导致的一个结果是,某些错误原本通常在运行期才能侦测到,现在可在编译期找出来.另一个结果是,使用TMP的C++程序可能在每一方面都 更高效:较小的可执行文件,较短的运行期,较少的内存需求.然而将工作从运行期转移到编译期的另一个结果是,编译时间变长了.程序如果使用TMP,其编译时间可能远长于不使用TMP的对应版本.
考虑 条款47导入的STL advance伪码:
template <typename IterT, typename DistT>
void advance(IterT& iter, DistT d) {
if (iter is random access iterator) {
iter += d; // 针对random access迭代器使用迭代器算术运算
} else {
if (d >= 0) { // 针对其他迭代器类型反复调用++或--
while (d--)
++iter;
} else {
while (d++)
--iter;
}
}
}
可以使用 typeid 让其中的伪码成真,取得C++对此问题的一个"正确"解决方案是所有工作都在运行期进行:
template <typename IterT, typename DistT>
void advance(Iter& iter, DistT d) {
if (typeid(typename std::iterator_traits<IterT>::iterator_category)
== typeid(std::random_access_iterator_tag)) {
iter += d;
} else {
if (d >= 0) {
while (d--)
++iter;
} else {
while (d++)
--iter;
}
}
}
条款47指出,
这个 typeid-based解法的效率比traits解法低,因为在此方案中,
(1)类型测试发生于运行期而非编译期,(2)"运行期类型测试"代码会出现在(或说被连接于)可执行文件中.实际上这个例子正彰显TMP如何能够比"正常的"C++程序更高效,因为traits解法就是TMP.
traits-based TMP解法针对不同类型而进行的代码,被拆分为不同的函数,每个函数所使用的操作(操作符)都可施行于该函数所对付的类型.
TMP的阶乘运算示范如何通过"递归模板具现化"(recursive template instantiation)实现循环,以及如何在TMP中创建和使用变量:
template <unsigned n>
struct Factorial { // 一般情况
enum { value = n * Factorial<n - 1>::value; }
};
template <>
struct Factorial<0> { // 特殊情况
enum { value = 1; }
};
循环发生在 template 具现体Factorial<n>内部指涉另一个 template 具现化Factorial<n - 1>时.和所有良好递归一样,需要一个特殊情况造成递归结束,这里的特殊情况是 template 特化体Factorial<0>.
为了领悟TMP之所以值得学习,很重要的一点是先对它能够达成什么目标有一个比较好的理解.下面举出三个例子:
1.确保量度单位正确.
2.优化矩阵运算. 条款21曾经提出某些函数包括 operator*必须返回新对象,而 条款44又导入了一个SquareMatrix class .考虑以下代码:
typedef SquareMatrix<double, 10000> BigMatrix;
BigMatrix m1, m2, m3, m4, m5; // 创建矩阵并赋予它们数值
...
BigMatrix result = m1 * m2 * m3 * m4 * m5; // 计算它们的乘积
以"正常的"函数调用动作来计算result,会创建4个暂时性矩阵,每一个用来存储对 operator*的调用结果.如果使用高级,与TMP相关的 template 技术,即所谓expression template,就有可能消除那些临时变量并合并循环,这一切都无需改变客户端的用法.于是TMP软件使用较少的内存,执行速度又有提升.
3.可以生成客户定制的设计模式(custom design pattern)实现品. 设计模式如Strategy(详见条款55),Observer,Visitor等等都可以多种方式实现出来.运用所谓policy-based design的TMP-based技术,有可能产生一些 template 用来表述独立的设计选项,然后任意结合它们,导致模式实现品带着客户定制的行为.这项技术已被用来让若干 template 实现出智能指针的行为政策(behavioral policies),用以在编译期间生成数以百计的智能指针类型.
注意:
Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以实现早期作物侦测和更高的执行效率.
TMP可被用来生成"基于政策选择组合"(based on combination of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码.