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;
else
while(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;
else
while(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;
else
while(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>的值是1
enum {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之所以值得学习,就要先对它能够达成什么目标有一个比较好的理解。下面举三个例子:
typedef SquareMatrix<double,1000> BigMatrix;
BigMatrix m1,m2,m3,m4,m5;
……
BigMatrix result=m1 * m2 * m3 * m4 * m5;
上面乘法会产生四个临时性矩阵,乘法还可能产生了4个作用在矩阵元素身上的循环。如果使用高级、与TMP相关的template(即expression templates),就有可能消除那些临时对象并合并循环。所以TMP使用较少内存,执行速度也有提升。
TMP目前还不完全成熟,语法不直观,支持的工具还不充分。但TMP对难以或甚至不可能于运行期实现出来的行为表现能力很吸引人。虽然TMP不会成为主流,但是会成为一些程序员(特别是程序库的开发人员)的主要粮食。
总结
《Effective C++》:条款48:认识template元编程
原文地址:http://blog.csdn.net/kangroger/article/details/44264053