一. 内容
-
template metaprogramming(TMP,模板元编程)是:
编写 template C++ 程序并执行于编译期的过程
。所谓模板元程序就是:以 C++ 写成,执行于 C++ 编译器内的程序。该程序执行后产生具现的代码,和正常代码一并加入编译。也就说元编程
可以做到用代码去生成代码
。 -
TMP 伟大之处在于,由于 template metaprograms 执行于 C++ 编译期,因此可以
将很多工作从运行期转移到编译期
。例如:- 某些错误原本通常在运行期才能检测到,现在可在编译器找出来。
- 使用 TMP 的 C++ 程序可能在每一方面都更加高效:比如较小的可执行文件,较短的运行期,较少的内存需求。
当然,将工作移至编译期,会导致编译时间变长,这是值得注意的一点。
-
在条款 47 我们曾提到如何实现一个 Move 函数:
template <typename IteratorType> void Move(IteratorType& Iterator, int Distance) { // 使用类型信息 if (typeid(IteratorTraits<IteratorType>::IteratorTag) == typeid(RandomAccessIteratorTag)) { Iterator += Distance; } else { if (Distance >= 0) { while (Distance--)++Iterator; } else { while (Distance++)--Iterator; } } }
并且我们曾描述这可能存在编译问题,现在就让我们看看是什么:虽然我们这里根据迭代器类型进行不同的操作,或 +=,或 ++,–,我们知道只有 Random Access Iterator 可以有 += 运算,但是 C++ 要求:
编译器必须确保所有源码都有效,即使是不会执行的源码
。也就是说编译器会拿着其他不支持 += 的迭代器,进入 if 语句先测试是否支持 += 运算,无效则会报错。所以相比于要支持所有操作,Traits class 针对不同类型进行函数重载的做法显然更好。
-
我们先简单了解一下 TMP 编程。TMP 已被证明是一个图灵完备(Turing-complete)机器,这意味着它可以计算任何事物,使用 TMP 你可以声明变量,执行循环,编写及调用函数…但这些相对于正常的 C++ 的实现会有很大的不同。比如:TMP 并没有循环部件,所有的循环效果都由
递归
完成。如果你不了解递归,恐怕必须先解决这个问题。 -
一个经典的入门案例是使用 TMP 计算阶乘:
template <unsigned N> struct Factorial { static const int Value = N * Factorial<N - 1>::Value; }; template <> struct Factorial<0> { static const int Value = 1; }; inline void TryWithFactorial() { std::cout << Factorial<10>::Value << "\n"; }
和所有递归行为一样,我们
需要一个特殊情况来结束递归
。对于 TMP 而言就是使用 tmeplate 的特化
版本。
正如 TryWithFactorial 函数所使用的,只要你声明 Factorial<N>::Value 就可以得到 N 阶乘值。当然这里存在值溢出的问题。 -
TMP 目前是一个
新生的技术
,语法不直观,也缺少 template 相关的调试器,但它将运行期工作移至编译期所带来的效率提升还是很令人印象深刻。
二. 总结
- Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率。
- TMP 可被用来生成基于政策选择组合(based on combination of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不合适的代码。