C++模板元编程

template<int Base>
class XY<Base, 0> 
{
public:
    enum { result_ = 1 };
};
模板元编程技术之根本在于递归模板实例化。第一个模板实现了一般情况下的递归规则。当用一对整数<X, Y>来实例化模板时,模板XY<X, Y>需要计算其result_的值,将同一模板中针对<X, Y-1>实例化所得结果乘以X即可。第二个模板是一个局部特化版本,用于终结递归。 
让我们看看使用此模板来计算5^4 (通过实例化XY<5, 4>)时发生了什么: 
// xytest.cpp

#include <iostream>
#include "xy.h"

int main() 
{
    std::cout << "X^Y<5, 4>::result_ = " << XY<5, 4>::result_;
}
首先,编译器实例化XY<5, 4>,它的result_为5 * XY<5, 3>::result_,如此一来,又需要针对<5, 3>实例化同样的模板,后者又实例化XY<5, 2>…… 当实例化到XY<5, 0>的时候,result_的值被计算为1,至此递归结束。 

递归模板实例化的深度和终结条件
 

可以想象,如果我们以非常大的Y值来实例化类模板XY,那肯定会占用大量的编译器资源甚至会迅速耗尽可用资源(在计算结果溢出之前),因此,在实践中我们应该有节制地使用模板元编程技术。 
虽然 C++标准建议的最小实例化深度只有17层,然而大多数编译器都能够处理至少几十层,有些编译器允许实例化至数百层,更有一些可达数千层,直至资源耗尽。 
假如我们拿掉XY模板局部特化版本,情况会如何? 
// xy2.h

//原始摸板

template<int Base, int Exponent>
class XY
{
public:
    enum { result_ = Base * XY<Base, Exponent-1>::result_ };
};
测试程序不变: 
// xytest2.cpp

#include <iostream>
#include "xy2.h"

int main() 
{
    std::cout << "X^Y<5, 4>::result_ = " << XY<5, 4>::result_;
}
执行如下编译命令: 
C:\>g++ -c xytest2.cpp 
你将会看到递归实例化将一直进行下去,直到达到编译器的极限。 
GNU C++ (MinGW Special) 3.2的默认实例化极限深度为500层,你也可以手工调整实例化深度: 
C:\>g++ -ftemplate-depth-3400 -c xytest2.cpp 
事实上,g++ 3.2允许的模板实例化极限深度还可以再大一些(我的测试结果是不超过3450层)。 
因此,在使用模板元编程技术时,我们总是要给出原始模板的特化版(局部特化版或完全特化版或兼而有之),以作为递归模板实例化的终结准则。 

利用模板元编程技术解开循环 

模板元编程技术最早的实际应用之一是用于数值计算中的解循环。举个例子,对一个数组进行求和的常见方法是: 
// sumarray.h

template <typename T>
inline T sum_array(int Dim, T* a)
{
    T result = T();
    for (int i = 0; i < Dim; ++i) 
    { 
        result += a[i];
    }
    return result;
}
这当然可行,但我们也可以利用模板元编程技术来解开循环: 
// sumarray2.h

// 原始模板

template <int Dim, typename T>
class Sumarray 
{
public:
    static T result(T* a)
    {
        return a[0] + Sumarray<Dim-1, T>::result(a+1);
    }
};

// 作为终结准则的局部特化版
template <typename T>
class Sumarray<1, T> 
{
public:
    static T result(T* a)
    {
        return a[0];
    }
};

用法如下: 

// sumarraytest2.cpp

#include <iostream>
#include "sumarray2.h"

int main()
{
    int a[6] = {1, 2, 3, 4, 5, 6};
    std::cout << " Sumarray<6>(a) = " << Sumarray<6, int>::result(a);
}
当我们计算Sumarray<6, int>::result(a)时,实例化过程如下: 
Sumarray<6, int>::result(a)
= a[0] + Sumvector<5, int>::result(a+1)
= a[0] + a[1] + Sumvector<4, int>::result(a+2)
= a[0] + a[1] + a[2] + Sumvector<3, int>::result(a+3)
= a[0] + a[1] + a[2] + a[3] + Sumvector<2, int>::result(a+4)
= a[0] + a[1] + a[2] + a[3] + a[4] + Sumvector<1, int>::result(a+5)
= a[0] + a[1] + a[2] + a[3] + a[4] + a[5] 
可见,循环被展开为a[0]  + a[1] + a[2] + a[3] + a[4] + a[5]。这种直截了当的展开运算几乎总是比循环来得更有效率。 
也许拿一个有着600万个元素的数组来例证循环开解的优势可能更有说服力。生成这样的数组很容易,有兴趣,你不妨测试、对比一下。 

模板元编程在数值计算程序库中的应用 

Blitz++之所以“快如闪电”(这正是blitz的字面含义),离不开模板元程序的功劳。Blitz++淋漓尽致地使用了元编程技术,你可以到这些文件源代码中窥探究竟: 
  • dot.h
  • matassign.h
  • matmat.h
  • matvec.h
  • metaprog.h
  •  product.h
  • sum.h
  •  vecassign.h 
让我们看看Blitz++程序库dot.h文件中的模板元程序:
 
template<int N, int I>
class _bz_meta_vectorDot {
public:
    enum { loopFlag = (I < N-1) ? 1 : 0 };

    template<class T_expr1, class T_expr2>
    static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)
    f(const T_expr1& a, const T_expr2& b)
    {
        return a[I] * b[I] + _bz_meta_vectorDot<loopFlag * N, loopFlag * (I+1)>::f(a,b);
    }
共5页 首页 上一页 [1] [2] [3] [4] [5下一页 尾页>
相关信息
相关评论
相关文章
字母检索 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z