《Effective C++》:条款48:认识template元编程

Template metaprogramming(TMP,模板元编程)是编写template-based C++程序,编译的过程。template metaprogramming是用C++写的模板程序,,编译器编译出具体化的过程。也就是说,TMP程序执行后,从templates具体化出来C++源码,不再是模板了。

TMP有两个作用,一是它让某些事更容易。例如编写STL容器,使用模板,可是存放任何类型元素。二是将执行在运行期的某些工作转移到了编译期。还有一个结果是使用TMP的C++程序可能在其他方面更高效:较小的可执行文件、较短的运行期、较少的内存需求。但是将运行期的工作转移到了编译期,编译期可能变长了。

再看一下条款 47中的advance伪码

template<typename Iter, typename DistT>void advance(IteT& iter,DistT d){if(iter is a random access iterator)iter+=d;else{if(d>=0)while(d–) ++iter;elsewhile(d++) –iter;}}

可以使用typeid让判断iter类型的伪码运行

template<typename Iter, typename DistT>void advance(IteT& 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;elsewhile(d++) –iter;}}

typeid-based解法效率比traits解法低,因为在此方案中,1类型测试发生在运行期而不是编译期,2运行期类型测试代码在(或被连接于)可执行文件中。这个例子可以说明TMP比正常的C++程序更高效,因为traits解法就是TMP。

一些东西在TMP比在正常的C++更容易,advance提供一个好例子。advance的typeid-based实现方式可能导致编译期问题

std::list<int>::iterator iter;……advance(iter,10);void advance(std::list<int>::iterator& iter,int d){if(typeid(typename std::iterator_traits<std::list<int>::iterator>::iterator_category)==typeid(std::random_access_iterator_tag))iter+=d;//错误else{if(d>=0)while(d–) ++iter;elsewhile(d++) –iter;}}

在+=这个操作符上是错误调用。因为list::iterator不支持+=,它是bidirectional迭代器。我们知道不会执行+=那一行,因为typeid那一行总是不相等;但是编译期要确保所有源码都有效,即使是不会执行的代码。traits-based TMP解法针对不同类型执行不同代码,不会出现上述问题。

TMP已被证明是个图灵完全机器,也就是说它的威力足以计算任何事物。可以使用TMP声明变量、执行循环、编写调用函数……。有时候这会和正常C++对应物看起来很是不同,例如条款 47展示的TMP if-else是由templas和其特化具体表现出来。不过那是汇编语言级的TMP。针对TMP设计的程序库(例如Boost’s MPL,**条款**55)提供更高级的语法。

为了再次认识下事物在TMP中如何运作,来看下循环。TMP没有真正循环,循环由递归(recursion)完成。TMP递归甚至不是正常的递归,因为TMP递归不涉及递归函数调用,而是涉及递归模板化(recursive template instantiation)。

TMP的起手程序是在编译期计算阶乘。TMP的阶乘运输示范如何通过递归模板具体化实现循环,以及如何在TMP中创建和使用变量

template<unsigned n>struct Factorial{enum {value=n*Factorial<n-1>::value};};template<>struct Factorial<0>{ //特殊情况,Factorial<0>的值是1enum {value=1};};

有了这个template metaprogram,只要指涉Factorial::value就可以得到n阶乘值。循环发生在template具体化Factorial内部指涉另一个template具体化Factorial之时。特殊情况的template特化版本Factorial<0>是递归的结束。

每个Factorial template具体化都是一个struct,每个struct都声明一个名字为value的TMP变量,用来保存当前计算所获得的阶乘值。TMP以递归模板具体化取代循环,每个具体化有自己一份value,每个value有其循环内适当值。

用Factorial示范TMP就像用hello world示范编程语言一样。为了领悟TMP之所以值得学习,就要先对它能够达成什么目标有一个比较好的理解。下面举三个例子:

确保量度单位正确。使用TMP就可以确保在编译期所有量度单位的组合都正确。优化矩阵运算。条款 21曾经提到过某些函数包括operator * 必须返回新对象,在条款 44中有一个SquareMatrix。如果这样使用 typedef SquareMatrix<double,1000> BigMatrix;BigMatrix m1,m2,m3,m4,m5;……BigMatrix result=m1 * m2 * m3 * m4 * m5;

上面乘法会产生四个临时性矩阵,乘法还可能产生了4个作用在矩阵元素身上的循环。如果使用高级、与TMP相关的template(即expression templates),就有可能消除那些临时对象并合并循环。所以TMP使用较少内存,执行速度也有提升。

可以生成客户定制之设计模式(custom design pattern)实现品。使用policy-based design之TMP-based技术,有可能产生一些templates用来表述独立的设计项(所谓policies),然后可以任意结合它们,导致模式实现品带着客户定制的行为。一个积极奋进的目标,一种矢志不渝的追求。

《Effective C++》:条款48:认识template元编程

相关文章:

你感兴趣的文章:

标签云: