码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2014-08-27 01:35:57      阅读:329      评论:0      收藏:0      [点我收藏+]

标签:style   blog   color   os   使用   io   for   div   cti   

以构建一个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小时...

 

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

标签:style   blog   color   os   使用   io   for   div   cti   

原文地址:http://www.cnblogs.com/fandingBlog/p/3938545.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!