简介
在《C++ Templates: The Complete Guide》一书中(以下简称书),提出了模板元编程最早的实际应用之一:在数值运算中进行解循环优化。
而本文的标题是噱头!本文的真正目的是指出这种优化措施在增加复杂性的同时,并不一定能明显改善效率。应当谨慎使用该技术——默认不使用该技术,在点积计算确实是效率瓶颈时考虑采用该技术,并认真测试该技术是否真能提高效率。
背景
数值运算库中常常需要提供向量点积(dot_product)运算。其定义用C++代码描述也许更清楚~
template<typename T>
我们可以使用这个函数,求2个向量的点积
T dot_product(int dim,const T v1[],const T v2[]) {
T result=0;
for (int i=0;i<dim;++i)
result += v1[i]*v2[i];
return result;
}int v1[] = {1,2,3};
得到r1=32
int v2[] = {4,5,6};
int r1 = dot_product(3,v1,v2);
书中指出:“这个结果是正确的,但是在要求高效率的应用中,它耗费了太多的时间”,对于这类特殊的问题,“简单的把循环展开”,如:r1=v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]
“反而会好得多”。
如何便捷的展开循环?将每个dot_product(dim,...) 手工重写成展开式?还是设计一整套函数: dot_product_dim_2,dot_product_dim_3,... ?
无疑这是一个冗长乏味易出错的方案。
书中提出了一种使用模板元编程解决该问题的方案,让我们来看看他是怎么做的。
模板元编程——解循环
// 首先是递归模板
我们可以使用DotProduct 来进行点积计算了:
template<int DIM,typename T>
struct DotProduct {
static T execute(const T v1[],const T v2[]);
}
template<int DIM,typename T>
T DotProduct<DIM,T>::execute(const T v1[],const T v2[]) {
return v1[0]*v2[0] + DotProduct<DIM-1,T>::execute(v1+1,v2+1);
}
// 递归边界模板
template<typename T>
struct DotProduct<1,T> {
static T execute(const T v1[],const T v2[]);
}
template<typename T>
T DotProduct<1,T>::execute(const T v1[],const T v2[]) {
return v1[0]*v2[0];
}int v1[] = {1,2,3}; int v2[] = {4,5,6};
int r2 = DotProduct<3,int>::execute(v1,v2);