首页 > 代码库 > 泛型编程深入探索之二,模板递归与可变参数模版
泛型编程深入探索之二,模板递归与可变参数模版
以构建一个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小时...
泛型编程深入探索之二,模板递归与可变参数模版
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。