您的位置:首页 > 编程语言 > C语言/C++

我的C++实践(7):模板元编程实战

2009-09-01 20:08 393 查看
    (1)计算整数的幂。

//pow.hpp:计算N的M次幂
#ifndef POW_HPP
#define POW_HPP
template<int N,int M>
class Pow{ //计算N的M次方的基本模板
public:
enum{ result=N*Pow<N,M-1>::result };
};
template<int N>
class Pow<N,0>{ //用于结束递归的局部特化
public:
enum{ result=1 };
};
#endif


    Pow<N,M>用来计算N的M次幂。实例化层次是Pow<N,M>,Pow<N,M-1>,...,Pow<N,0>,共M+1层。我们始终要记住编译器对模板元编程中的实例化层次是有限制的(否则会层次太深的话会耗尽编译器的可用资源),我测试出gcc 4.2.4最多只允许31层的实例化,因此这里如果使用Pow<2,31>,Pow<5,40>等,则编译不会通过。

    (2)求整数的平方根(向上取整)。可以用二分查找算法来查找,也可以用普通的从0开始迭代的查找算法。

//sqrt1.hpp:求整数的平方根(向上取整),用二分查找算法
#ifndef SQRT_HPP
#define SQRT_HPP
#include "ifthenelse.hpp"
template<int N,int LO=0,int Hi=N>
class Sqrt{
public:
enum{ mid=(LO+HI+1)/2 }; //计算中点
//执行二分查找:用递归模板,找到结果的类型,然后返回结果
//enum{ result=(N<mid*mid)? Sqrt<N,LO,mid-1>::result : Sqrt<N,mid,HI>::result };
typedef typename IfThenElse<(N<mid*mid),
Sqrt<N,LO,mid-1>,
Sqrt<N,mid,HI> >::ResultT SubT;
enum{ result=SubT::result }; //返回最终结果
};
template<int N,int M>
class Sqrt<N,M,M>{ //终止递归的局部特化:适用于LO等于HI
public:
enum{ result=M };
};
#endif


//sqrt2.hpp:求整数的平方根(向上取整),用迭代查找算法
#ifndef SQRT_HPP
#define SQRT_HPP
#include "ifthenelse.hpp"
template<int N>
class Value{ //包装常量N的模板,使N变成一个类型Value<N>
public:
enum{ result=N }; //定义其要返回的结果reuslt
};
template<int N,int I=0>
class Sqrt{ //基本模板,从0开始迭代,以找到N的平方根(向上取整)
public:
//执行迭代来找到最终结果的类型:用递归模板
typedef typename IfThenElse<(I*I<N),
Sqrt<N,N+1>,
Value<I> >::ResultT SubT;
enum{ result=SubT::result; } //返回最终找到的结果
};
#endif


    在模板元编程中,我们要尽量少用条件运算符?:来执行路径选择,因为这会导致两个分支中的递归模板都会被实例化,产生数量庞大的实例化体。例如使用sqrt1.hpp中注释掉的那句,当实例化Sqrt<N,LO,HI>时,会导致Sqrt<N,LO,mid-1>和Sqrt<N,mid,HI>的完全实例,这样最终的实例化个数大约是N的2倍。我们应该用模板特化来执行路径选择,例如我们使用前面类型萃取技术中介绍的IfThenElse<bool,T1,T2>模板。在它的特化中ResultT只返回其中一个类型(如T1),注意把T1类型typedef成ResultT并不会导致被实例化。当最终查找结果ResultT::resut时,就会实例化ResultT,可见这只实例化了一个IfThenElse中的一个分支。最终的实例化个数趋向于lg(N)。另外,在二分查找的实现中,返回是其中的一个分支类型,最后返回时应该是mid=HI时的Sqrt<N,mid,HI>,因此必须提供一个mid=HI的特化来结束Sqrt模板的递归,否则还会再去实例化Sqrt<N,mid,HI>。而在迭代查找的实现中,当I*I>=N时模板递归结束,这时返回Value<I>分支,用Value<I>::result直接返回计算出的结果,递归结束。因此不需要提供Sqrt的特化来结束递归。

    (3)计算向量的点乘。

//dotproduct.hpp:向量的点乘
#ifndef DOTPRODUCT_HPP
#define DOTPRODUCT_HPP
template<int DIM,typename T>
class DotProduct{ //基本模板
public:
static T result(T* a,T* b){
//第1个元素的乘积加到剩下的新向量的点乘
return *a * *b+DotProduct<DIM-1,T>::result(a+1,b+1);
}
};
template<typename T>
class DotProduct<1,T>{ //作为结束条件的局部特化
public:
static T result(T* a,T* b){
return *a * *b; //最后只有一个元素相乘
}
};
//包装函数:为了使用方便
template<int DIM,typename T>
inline T dot_product(T* a,T* b){
return DotProduct<DIM,T>::result(a,b);
}
#endif


//dotproducttest.cpp:向量点乘的测试
#include <iostream>
#include "dotproduct.hpp"
int main(){
int a[3]={1,2,3};
int b[3]={5,6,7};
std::cout<<"dot_product<3>(a,b) = "<<dot_product<3>(a,b)<<'/n';
std::cout<<"dot_product<3>(a,a) = "<<dot_product<3>(a,a)<<'/n';
return 0;
}


    (4)判断一个数是否是素数。

//isprime.hpp:判断一个数是否是素数
#ifndef IS_PRIME_HPP
#define IS_PRIME_HPP
template<int p,int i>
class is_prime{ //基本模板:判断p是否是素数
public:
enum{ prim=(p==2)||(p%i) && is_prime<(i>2?p:0),i-1>::prim };
};
template<>
class is_prime<0,0>{ //用于结束模板递归的全局特化
public:
enum{ prim=1 };
};
template<>
class is_prime<0,1>{ //用于结束模板递归的全局特化
public:
enum{ prim=1 };
};
#endif


    (5)打印连续的素数。

//primeprinter.hpp:打印连续的素数
#ifndef PRIME_PRINTER_HPP
#define PRIME_PRINTER_HPP
#include <iostream>
#include "isprime.hpp"
template<int i>
class PrimePrinter{ //打印i以下的连续素数
public:
enum{ prim=is_prime<i,i-1>::prim };
static void print(){
if(prim)
std::cout<<i<<std::endl;
PrimePrinter<i-1>::print();
}
};
template<>
class PrimePrinter<1>{ //用于结束模板递归的全局特化
public:
enum{ prim=0 };
static void print(){
return;
}
};
#endif


    总结出模板元编程的计算完整性:

    1)状态变量:也就是模板参数

    2)迭代构造(相当于循环语句):通过递归模板

    3)路径选择(相当于条件语句):通过使用特化或条件表达式?:

    4)整型对象(相当于变量):用enum定义的枚举值

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息