首页 > 代码库 > 泛型编程深入探索之二,模板递归与可变参数模版

泛型编程深入探索之二,模板递归与可变参数模版

以构建一个n纬网格为例,讲述模板递归。

 

首先是一个简单的一纬网格的实现,这个网格实现了规定长度的网格的实例化,并且能够在不同大小的网格类中自由的转型(通过模版嵌套的cast_ctr)

(使用到的技术,非类型参数模版,模版嵌套,类模版特例化,模版友元函数)

#include <cassert>#include <iostream>using namespace std;template <typename T,int LENGTH>class grid;template <typename T,int LENGTH>ostream& operator<< (ostream& os, const grid<T, LENGTH>& gd){    os<<"[[grid with size: "<<gd.getSize()<<"] ";    for(int i=0;i<gd.getSize()-1;i++){        os<<gd[i]<<"\t";    }    os<<gd[gd.getSize()-1]<<"]"<<endl;    return os;}template <typename T,int LENGTH>class grid{public:    grid():size(0),mCells(new T[LENGTH]){};    grid(const grid<T,LENGTH>& gd):mCells(new T[LENGTH]),size(gd.size){        for(int i=0;i<gd.size;i++){            mCells[i]=gd.mCells[i];        }    }    //足够,任何修改了t或者length的都会被这个嵌套模板handle    template<typename E, int NEWLENGTH>    //required E->T naturally    grid(const grid<E,NEWLENGTH>& gd):mCells(new T[LENGTH]),size(0){        int newSize=LENGTH>gd.getSize()?gd.getSize():LENGTH;            for(int i=0;i<newSize;i++){                *(mCells+i)=gd[i];            }            size=newSize;    }    grid<T,LENGTH>& operator=(const grid<T,LENGTH>& gd){        size=gd.getSize();        T* su1= mCells;        T* su2= gd.mCells;        while(su1!=mCells+gd.size-1){            *su1++=*su2++;        }    }    template<typename E, int NEWLENGTH>    //required E->T naturally    grid<T,LENGTH>& operator=(const grid<E,NEWLENGTH>& gd){        int newSize=LENGTH>gd.getSize()?gd.getSize():LENGTH;        for(int i=0;i<newSize;i++){            *(mCells+i)=gd[i];        }        size=newSize;    }    inline int getSize()const {return size;}    virtual ~grid(){delete mCells;mCells=nullptr;};    T& operator[](int index){        if(index>=size){            resize(index+1);        }        return *(mCells+index);    }    const T& operator[] (int index)const{        assert(index<getSize());        return *(mCells+index);    }    void resize(int newSize,const T& def=T()){        assert(newSize<=LENGTH);        if(newSize<=size){            size=newSize;        }        else {            int i=size;            for(;i<=newSize-1;i++){                *(mCells+i)=def;            }            size=newSize;        }    }    friend ostream& operator<< <T,LENGTH>(ostream& os, const grid<T, LENGTH>& gd);private:    T* mCells;    int size;};

测试代码如下:

#define     _TEST_GRID_      1#if         _TEST_GRID_#include "grid.h"#include <iostream>#include <ctime>#include <cstdio>using namespace std;int main(){        //网格实例化    grid<int,20> a;    srand((int)time(NULL));    for(int i=0;i<20;i++){        a[i]=rand()%30;    }    //不同大小,不同类型的网格互相拷贝    grid<double,40>b(a);    grid<double,10>c(a);    cout<<a<<b<<c;    //利用一纬网格,由调用方实现模版递归(实际是实例递归)    grid<int,40> onegrid;    grid<grid<int, 40>, 40> twogrid;    unsigned long lct=time(NULL);    for(int k=0;k<40;k++){        srand((unsigned)(lct-clock()));        for(int i=0;i<20;i++){            twogrid[k][i]=rand()%30;        }    }    cout<<twogrid;}#endif

实例递归的好处是语义上简单,容易理解,缺点却很多:

1)  使用hadrcode的初始化列表才能对每一个网格进行初始化。

2)    所有子网格的大小类型必须保持一致。

 

另外一种递归是由类的设计方所实现的,借助嵌套和递归设计技术,设计方可以设计出简洁明了的任意纬度的网格类(而不是由使用者来实现),且不需要对每一纬度单独进行特例化。

 

#include <iostream>#include <cassert>template <typename T,int LENGTH, int N>class multigrid{public:    multigrid():size(0),mCells(new multigrid<T, LENGTH, N-1>[LENGTH]){};    virtual ~multigrid(){delete[] mCells;mCells=nullptr;}    multigrid(const multigrid<T,LENGTH,N>& gd):size(0),mCells(new multigrid<T, LENGTH, N-1>[LENGTH]){        mCells=gd.mCells;        size=gd.size;    }    //基础,同一纬度下的拷贝    template <typename E,int NEWLENGTH>    multigrid(const multigrid<E,NEWLENGTH,N>& gd):size(0),mCells(new multigrid<T, LENGTH, N-1>[LENGTH]){        int newSize=gd.getSize()>LENGTH?LENGTH:gd.getSize();        for(int i=0;i<newSize;i++){            mCells[i]=gd[i];        }        size=newSize;    }    int getSize()const{return size;}    multigrid<T, LENGTH, N-1>& operator[](int index){        assert(index<LENGTH);        if(index>=size){            resize(index+1);        }        return mCells[index];    }    const multigrid<T, LENGTH, N-1>& operator[](int index) const{        assert(index<size);        return mCells[index];    }    void resize(int newSize,const multigrid<T, LENGTH, N-1>& def=multigrid<T, LENGTH, N-1>()){        assert(newSize<=LENGTH);        if(newSize<=size)            size=newSize;        else {            for(int i=size;i<newSize;i++){                mCells[i]=def;            }            size=newSize;        }    }    multigrid<T,LENGTH,N>& operator=(const multigrid<T,LENGTH,N>& gd){        if(this==&gd)            return *this;        size=gd.size;        for(int i=0;i<size;i++){            mCells[i]=gd.mCells[i];        }        return (*this);    }    template<typename E, int NEWLENGTH>    multigrid<T,LENGTH,N>& operator=(const multigrid<E,NEWLENGTH,N>& gd){        int newSize=LENGTH>gd.getSize()?gd.getSize():LENGTH;        for(int i=0;i<newSize;i++){            *(mCells+i)=gd[i];        }        size=newSize;        return (*this);    }private:    multigrid<T, LENGTH, N-1>* mCells;    int size;};//利用偏特化技术构造一个递归基template <typename T,int LENGTH>class multigrid <T, LENGTH,1>{public:    multigrid():size(0),mCells(new T[LENGTH]){};    virtual ~multigrid(){delete [] mCells;mCells=nullptr;};    multigrid(const multigrid<T,LENGTH,1>& gd):size(0),mCells(new T[LENGTH]){        for(int i=0;i<gd.size;i++){            mCells[i]=gd.mCells[i];        }        size=gd.size;    }    int getSize()const{return size;}    template <typename E,int NEWLENGTH>    multigrid(const multigrid<E,NEWLENGTH,1>& gd){        int newSize=gd.getSize()>LENGTH?LENGTH:gd.getSize();        for(int i=0;i<newSize;i++){            mCells[i]=gd[i];        }        size=newSize;    }    T& operator[](int index){        assert(index<LENGTH);        if(index>=size){            resize(index+1);        }        return mCells[index];    }    const T& operator[] (int index) const{        assert(index<size);        return mCells[index];    }    void resize(int newSize,const T& def=T()){        assert(newSize<=LENGTH);        if(newSize<=size)            size=newSize;        else {            for(int i=size;i<newSize;i++){                mCells[i]=def;            }            size=newSize;        }    }    multigrid<T,LENGTH,1>& operator=(const multigrid<T,LENGTH,1>& gd){        if(this==&gd)            return *this;        size=gd.size;        for(int i=0;i<size;i++){            mCells[i]=gd.mCells[i];        }        return (*this);    }    template <typename E,int NEWLENGTH>    multigrid<T,LENGTH,1>& operator=(const multigrid<E,NEWLENGTH,1>& gd){        int newSize=gd.getSize()>LENGTH?LENGTH:gd.getSize();        for(int i=0;i<newSize;i++){            mCells[i]=gd[i];        }        size=newSize;        return (*this);    }private:    T* mCells;    int size;};

 

通过模版递归和偏特化递归基,我们可以实现任意纬的网格。

 

特别值得注意的是,无论是在静态时刻递归产生多维网络,还是动态时刻递归产生多维网络,效率是一样的,因为他们都是一种迭代产生所有实例对象的过程,但由于这些实例对象全部是用户所需要的,并没有产生大量重复,因此,这种效率是可以接受的。

 

总而言之,与inline不同,模版递归并不是一种牺牲空间换时间的策略!!!他造成的是确确实实的代码膨胀。

事实上,按照上述方法,经笔者测试,制作一个6维矩阵长度为40的六维矩阵的时间为26s,也就说,制作一个10维则需要440小时...

 

泛型编程深入探索之二,模板递归与可变参数模版