泛型编程 实例 阶乘

来源:互联网 发布:淘宝服装设计 编辑:程序博客网 时间:2024/05/29 09:04

1-递归求阶乘

模板元编程:编译期得到结果

步骤:

1-递归模板具现化实现循环

template<unsigned n>

struct factorial{

enum{value =n*factorial<n-1>::value};

};

2-模板特化结束循环

template<>

struct factorial<0>{

enum {value=1};

};

 

调用factorial<n>::value

调用factorial<10>::value

2- 求3的N次方  

原理

  模板元编程的工作:递归的模板实例化

计算3^N的递归模板实例化应用以下2个规则:

1.3^N=3*3^(N-1)

2.3^0=1

枚举值实现

Step1:第1个模板实现一般的递归原则

     template<int N>

     class pow1{

public:

enum{result=3*pow3<N-1>::result};

};

Step2:第2个模板结束递归的特化,确定pow3<0>的结果

     template< >

     class pow1<0>{

public:

enum{result=1};

};

效果:调用pow3<7>::result

编译器实例化pow3<7>,获得3*pow3<6>::result

      实例化pow3<6>,获得3*pow3<5>::result

      类似的,递归不断进行,直到pow3<>基于0进行实例化,递归结束,

      1作为pow3<7>的结果

静态常量实现

step1-计算3的N次幂的基本模板

templ<int N>

class pow3{

public :

static int constresult=3*pow3<N-1>::result;

};

step2-用于结束递归的局部特化

template<>

class pow3<0>{

public:

 static int const result=1;

};

缺点:静态成员变量只能是左值,

因此,如果声明如下:

void foo(int const &);

把模板元程序的结果传递时,即:

foo(pow3<7>::result);

编译器必须传递pow3<7>::result的地址,----》这会强制编译器实例化静态成员的定义,并为定义分配内存

 

但枚举值却不是左值(它们没有地址)。

所以:通过引用传递枚举值时,不使用任何静态内存,[<template C++>中使用枚举值]

计算平方根

原理

递归过程是由二分查找。

使用?:判断位于LO,Hi的前半部分还是后半部分

如果mid^2大于N,在前半部分进行查找;否则在后半部分查找

递归结束条件:LO,HI具有相同的值M,其中M就是最终结果

步骤

step1-sqrt(N)的基本模板

template<int N, int LO=1,int HI=N>

class mysqrt{

public:

enum{mid=(LO+HI+1)/2};//计算中点

//二分查找一个较小的result

enum{result=(N<mid*mid)?:mysqrt<N,1,mid-1>::result

:mysqrt<N,mid+1,N>::result};

};

step2-局部特化,适用于LO==HI

template<int N,int M>

class mysqrt<N,M,M>{

public:

enum{result=M};

};

实例化—扩展过程

mysqrt<16>::result

扩展为: mysqrt<16,1,16>::result

        直到mysqrt<16,4,4>::result结束了整个递归

缺点:完全实例化

当编译器计算下面表达式的时候:

(16<8*8)?:mysqrt<16,1,8>::result

           : mysqrt<16,9,16>::result

编译器实例化了mysqrt<16,1,8>,

     也实例化了mysqrt<16,9,16>,完全实例化mysqrt<16,9,16>将会促使mysqrt<16,9,12>,mysqrt<16,13,16>的完全实例化

 最终实例化题总数大约是N的2倍

更好的实现 ifelse

1-基本模板

template<bool C,typename TA,typename TB>

class ifthenelse;

2-局部特化,true意味着选择第2个实参

template< typename TA,typename TB>

class ifthenelse<true,TA,TB>

{

public:

typedef TA result;

};

3-局部特化,false意味着选择第3个实参

template< typename TA,typename TB>

class ifthenelse<false,TA,TB>

{

public:

typedef TB result;

};


step1-sqrt(N)的基本模板

template<int N, int LO=0,int HI=N>

class mysqrt{

public:

enum{mid=(LO+HI+1)/2};//计算中点

使用二分法查找一个较小的值

typedef typename ifthenelse<N<mid*mid>,mysqrt<N,LO,HI>,mysqrt<N,mid,HI>>::result

       subt;

 enum{result=subt::result};

};

step2-局部特化,适用于LO==HI

template<int N,int M>

class mysqrt<N,M,M>{

public:

enum{result=M};

};

或者等价于下面实现

template<int N,int S>

class mysqrt<N,S,S>

{

public:

enum{result=S};

};

现在:不会完全实例化了,实例化体数量趋向于log2(N)

自然且迭代的算法

原理

         为了计算N的平方根,使用迭代,I的值从0迭代到N,直到I^2 >=N

       此时,I即是N的平方根(不考虑不存在平方根的情况)

C++实现

int I;

for(I-0;I*I<N;++I)

{

;

}

//I是N的平方根

实现:

step1-迭代计算平方根的基本模板

template<intN,int I=0>

class sqrt{

public:

enum{result=I*I<N?:sqrt<N,I+1>::result

:I;}

};

用于结束迭代的局部特化

template<intN>

classsqrt<N,N>{

public:

  enum{result=N};

};

若I*I<N,下次迭代sqrt<N,I+1>::result作为本次的result;

否则将I作为本次的result

计算sqrt<4>的实例化过程

result=(1*1<4)?sqrt<4,2>::result:1

result=(1*1<4)?(2*2<4)?sqrt<4,3>::result:2:1

以此类推,直到找到结束递归的特化才结束,

也就是说,如果没有提供特化,编译器将继续进行实例化,直到达到编译器内部实例化个数的最大值。

IFTHENELSE模板解决:

step1-模板参数作为result的基本模板

template<int N >

class value{

public:

enum{result=N};

};

 

step2-迭代计算平方根

template<intN,int I=0>

class sqrt{

public:

typedef typenameifthenelse<(I*I<N),sqrt<N,I+1>,value<I>

                           

                           >::result

            subt;

enum{result=subt::result};

};

优点:

使用ifthenelse模板后,实例化的数量接近于sqrt(N),不是原来的N了

通过上面例子,可总结出模板元编程的几个部分:

模板参数

迭代构造:通过递归

路径选择:使用条件表达式或特化

整型(即枚举里的值应该为整型)

如果对递归实例化体和模板参数的数量没有限制,对于编译期可计算的任何对象,都可以使用元编程;

但C++标准建议最多进行17层的递归实例化,然而大多数编译器都能够处理至少几十层,有些编译器答应实例化至数百层,更有一些可达数千层,直至资源耗尽。

实际开发中,很少使用模板 元编程(有些情况下高效)

0 0