首页 > 代码库 > 细说C++模板元编程

细说C++模板元编程

模板元编程根在模板,模板的使用很简单:为自动代码生成提供方便,提高程序员生产率的一个非常有效的方法就是代码复用,而面向对象很重要的一个贡献就是通过内部紧耦合和外部松耦合将思想转化成一个个容易复用的概念,但是面向对象提供的工具箱里面包含了所包含的继承,组合和多态并不能完全满足实际编程总对于代码复用的全部要求,与市模板就应运而生了。

模板时更智能的宏,模板和红都是编译前生成代码,模板和红相比,它站在更高的抽象上上面,宏操作的是字符串token,然而模板却能够操作c++中的类型,所以模板更安全,更智能

说完模板,再说模板元编程,模板元编程其实就是复杂点的模板,简单的模板在特化时即基本只包含类型的查找与替换,这种模板可以看做是类型安全的宏,而模板元编程就是将一些通常编程时才有的概念比如:递归,分支等加入模板特化过程中的模板,但其实说白了,还是模板,自动代码生成而已。

 

C++的模板元编程(TMP)的基本原则就是:将负载由运行时转移到编译时,同时保持原有的抽象层次。其中负载可以分为两类,一类就是程序运行本身的开销,一类则是程序员需要编写的代码。前者可以理解为编译时优化,后者则是为提高代码复用度,从而提高程序员的编程效率。

 

假如我们用float**代表着一个多路的一维信号,那么假如我们要对两个这样的多路信号进行拷贝,
用C++,一般我们这样写:

//float** in; float** out;for( size_t ch=0 ; ch<channelNum ; ++ch ) {    for( size_t i=0; i<length ; ++i ) {        out[ch][i]=in[ch][i];    }}

这么写挺标准,没什么问题,但是,作为一个性能狂魔,我表示不爽.
首先,如果在编译期我能确定channelNum是个确定的值,比如说是4,那么上面的代码可以优化成: 

for(size_t i=0;i<length;++i) {    out[0][i]=in[0][i];    out[1][i]=in[1][i];    out[2][i]=in[2][i];    out[3][i]=in[3][i];}

这样做的好处是至少有两个,1.优化了分支, 2.优化了数据缓存刷新 .带来了性能提升,但代码被写死了,channelNum必须是4,万一我需要3,或者5怎么办? 这时模板元就派上用场了:

   template<int count>class Copy{public:        static inline go(float* const out, float* const, int i)		{		    Copy<count-1>::go(out,in,i);			out[count][i-1]=in[count][i];		}};template<>class Copy<0>{    public:	     static inline go(float* const,float* const,int)		 {}};

  

 这样,无论cout参数为多大,每当运行"go"函数时,go必然要再次运行Copy<cout-1>::go.
这是一个递归结构,我就用它构建了一个能够自动生成代码的模板类"Copy",然后我这样写:

template<int channelNum>void palll_copy(float** out,float** in,size_t length){    for(size_t i=0;i<length;++i) {        Copy<channelNum>::go(out,in,i);    }}

  综上,模板元最大的好处就是,当你需要一个极端追求性能,甚至连一般的C++代码的性能都满足不了你的时候,

请用汇编,但是当你既要更强的性能,又要更大的自由度和代码复用,当你妄图榨干编译器的最后一点点节操,当你妄图虐待编译器使其经常弹出模板上下文太复杂,当你是如此有理想,以至于不惜使编译时间狂涨10倍换来用户0.1ms的性能提升时,可以考虑用模板元。

 

细说C++模板元编程