template metaprogramming (TMP)(模板元编程)是写 template-based(基于模板)的运行于编译期间的 C++ 程序的过程。考虑一下:一个 template metaprogram(模板元程序)是用 C++ 写的运行于 C++ 编译器中的程序。当一个 TMP 程序运行完成,它的输出——从 templates(模板)实例化出的 C++ 源代码片断——随后被正常编译。
如果你仅把它看作古怪的特性而没有打动你,那你就不会对它有足够的深入的思考。
C++ 并不是为 template metaprogramming(模板元编程)设计的,但是自从 TMP 在 1990 年代早期被发现以来,它已被证明非常有用,使 TMP 变容易的扩展很可能会被加入到语言和它的标准库之中。是的,TMP 是被发现,而不是被发明。TMP 所基于的特性在 templates(模板)被加入 C++ 的时候就已经被引进了。所需要的全部就是有人注意到它们能够以一种精巧的而且意想不到的方式被使用。
TMP 有两个强大的力量。首先,它使得用其它方法很难或不可能的一些事情变得容易。第二,因为 template metaprograms(模板元程序)在 C++ 编译期间执行,它们能将工作从运行时转移到编译时。一个结果就是通常在运行时才能被察觉的错误能够在编译期间被发现。另一个结果是 C++ 程序使得 TMP 的使用在以下每一个方面都能更有效率:更小的可执行代码,更短的运行时间,更少的内存需求。(然而,将工作从运行时转移到编译时的一个结果就是编译过程变得更长。使用 TMP 的程序可能比它们的 non-TMP 对等物占用长得多的编译时间。)
考虑STL的advance伪代码。(在《C++箴言:为类型信息使用特征类》中。你现在可能需要读该文,因为在本文中,我假设你已经熟悉了该文的内容。),我突出表示代码中的伪代码部分:
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if (iter is a random Access iterator) {
iter += d; // use iterator arithmetic
} // for random access iters
else {
if (d >= 0) { while (d--) ++iter; } // use iterative calls to
else { while (d++) --iter; } // ++ or -- for other
} // iterator categories
}
我们可以用 typeid 把伪代码变成真正的代码。这就产生了一个解决此问题的“常规”的 C++ 方法——它的全部工作都在运行时做:
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if (typeid(typename std::iterator_traits<IterT>::iterator_category) ==
typeid(std::random_access_iterator_tag)) {
iter += d; // use iterator arithmetic
} // for random access iters
else {
if (d >= 0) { while (d--) ++iter; } // use iterative calls to
else { while (d++) --iter; } // ++ or -- for other
} // iterator categories
}
《C++箴言:为类型信息使用特征类》中指出这个 typeid-based(基于 typeid)的方法比使用 traits 的方法效率低,因为这个方法,(1)类型检测发生在运行时而不是编译期,(2)用来做运行时类型检测的代码必须出现在可执行代码中。实际上,这个例子展示了 TMP 如何能比一个“常规”C++ 程序更高效,因为 traits 方法是 TMP。记住,traits 允许编译时在类型上的 if...else 计算。
我先前谈及一些事情在 TMP 中比在“常规”C++ 中更简单,而 advance 提供了这方面的一个例子。Item 47 提到 advance 的 typeid-based(基于 typeid)的实现可能会导致编译问题,而这就是一个产生问题的例子: