C++模板元编程之使用模板,对数组进行“编译期间求和”
来源:互联网 发布:房产销售软件 编辑:程序博客网 时间:2024/06/07 14:43
编译期间求值,将计算提前到编译期间进行,可以最大限度地榨干编译器的潜力,提高程序的运行速度,用Andrei Alexanderescu的话说就是“时间花在编译期,就某种意义来说这是‘免费的’”(《C++设计新思维》P55)。所以就有了对数组在编译期间求和的需求。
先上代码,再解释:
#include <iostream>using namespace std;//一个全局的int数组,需要对其求和const int CONST_ARRAY[5]={1,2,3,4,5};//去掉const属性后,编译也可以通过//一个类模板,用于对数组求和template<int index>struct SumArrInCompilePhaseCls{static const int SUM;};//通过递归定义模板的方式求SUM的值template<int index>const int SumArrInCompilePhaseCls<index>::SUM=CONST_ARRAY[index]+SumArrInCompilePhaseCls<index-1>::SUM;//亮点在这里//一个完全特化的类模板,用于结束模板的递归定义template<>struct SumArrInCompilePhaseCls<0>{static const int SUM;};const int SumArrInCompilePhaseCls<0>::SUM=CONST_ARRAY[0];int main(){ //使用模板,进行编译期间的数组求和cout<<SumArrInCompilePhaseCls<4>::SUM<<endl;//结果15}SUM确实是在编译阶段计算的,因为SUM是static const类型,该类型在编译完成之后,就固定了,无法在运行期间改变!
仔细研究上面一段代码,就会发现,在定义非特化类模板的SUM值时,使用了模板的递归定义:
//通过递归定义模板的方式求SUM的值template<int index>const int SumArrInCompilePhaseCls<index>::SUM=CONST_ARRAY[index]+SumArrInCompilePhaseCls<index-1>::SUM;编译器在编译该表达式时,由于SumArrInCompilePhaseCls<index-1>类型未知,所以编译器会生成SumArrInCompilePhaseCls<index-1>,继而生成SumArrInCompilePhaseCls<index-2>,SumArrInCompilePhaseCls<index-3>,……还有好多~。也就是说,该表达式,导致编译器递归生成多个SumArrInCompilePhaseCls模板类,而当index终于递减到0时,即要生成SumArrInCompilePhaseCls<0>时,编译器“惊喜”地发现,SumArrInCompilePhaseCls<0>已经实现了!所以编译器就停止继续生成"SumArrInCompilePhaseCls模板类"了,递归结束了!
由于所有“SumArrInCompilePhaseCls模板类”里的SUM都是static const值,所以对于const值,就可以在编译期间求和。
结束了吗?
木有!
上面的代码有两个小问题:
1、调用时,输入的index如果为负数(比如-1),编译期间出现递归栈溢出,编译失败!因为负数无法“递减到0",也就是说,递归不会停止!
2、输入的index超过CONST_ARRAY的上限时(比如5),尽管编译通过,但是运行结果错误,因为编译期间求和的时候数组越界了!
好了,我们希望对数组求和的时候,可以判断用户的输入是否合法!
可以通过增加一个“编译期间的断言”来解决这两问题,这个断言的源码如下:
//一个编译期间起断言作用的类模板,用于保证数组下标始终非负template<bool isOk>struct CompilePhaseAssertion;template<>struct CompilePhaseAssertion<true>{};
使用的时候,可以这样子:
//一个类模板,用于对数组求和template<int index>struct SumArrInCompilePhaseCls{static const int SUM;private:CompilePhaseAssertion<index>=0&&index<sizeof(CONST_ARRAY)/sizeof(int)> IsValidIndex;};原理很明显了吧?
CompilePhaseAssertion是个类模板,但是并没有定义它!仅仅有一个参数为true的完全特化版本,所以,当模板参数isOk为false时,根本无法生成这种类型的实例,因为没有isOk==false的模板类,因而导致编译报错。
通过在CompilePhaseAssertion中增加CompilePhaseAssertion实例的方式,保证index的范围合法。
修改后的代码如下:
#include <iostream>using namespace std;//一个全局的int数组,需要对其求和int CONST_ARRAY[5]={1,2,3,4,5};//一个编译期间起断言作用的类模板,用于保证数组下标始终非负template<bool isOk>struct CompilePhaseAssertion;template<>struct CompilePhaseAssertion<true>{};//一个类模板,用于对数组求和template<int index>struct SumArrInCompilePhaseCls{static const int SUM;private:CompilePhaseAssertion<index>=0&&index<sizeof(CONST_ARRAY)/sizeof(int)> IsValidIndex;};//通过递归定义模板的方式求SUM的值template<int index>const int SumArrInCompilePhaseCls<index>::SUM=CONST_ARRAY[index]+SumArrInCompilePhaseCls<index-1>::SUM;//一个完全特化的类模板,用于结束模板的递归定义template<>struct SumArrInCompilePhaseCls<0>{static const int SUM;};const int SumArrInCompilePhaseCls<0>::SUM=CONST_ARRAY[0];int main(){//使用模板,进行编译期间的数组求和cout<<SumArrInCompilePhaseCls<4>::SUM<<endl;}
此时,如果使用以下两种方式求和,则编译报错:
cout<<SumArrInCompilePhaseCls<-1>::SUM<<endl;//数组下标为负cout<<SumArrInCompilePhaseCls<5>::SUM<<endl;//数组上界越界
正文完。
参考资料:
1、《C++设计新思维》,Andrei Alexanderescu著,侯捷、於春景译。
2、C++模板元编程技术与应用
3、荣威老师写的其他文章或书籍
4、C++ 的MetaProgramming 入门篇
注意:
以上代码在VS2005和G++中都能编译通过,但是只有G++编译出来的程序才能运行出正确结果(15),VS2005中的结果是错误的(5),原因还不清楚,如果您知道VS2005计算错误的原因,请及时留言通知我,我会非常感激您的!
- C++模板元编程之使用模板,对数组进行“编译期间求和”
- 模板元编程-编译期间加密
- C++模板元编程之编译期间“产生从2到某个给定值之间的所有质数”
- C++11 模板元编程初探: 在编译期间确定斐波那契数列
- C++模板元编程之模板递归
- 模板--模板元编程
- C++之模板元编程
- 模板元编程-编译期计算数值
- 模板元编程 运行时 编译期
- 【C++】traits技术与模板元编程
- C++之:模板元编程(一)
- effective C++之模板元编程
- C++箴言:谨慎使用模板元编程
- C++箴言:谨慎使用模板元编程
- C++之:模板元编程(二) 模板形参
- C++之:模板元编程(三) 默认模板参数
- 初探模板元编程
- 模板元编程
- poj 2992 Divisor
- extern “C”
- win32 GDI的那点事
- Yet Another Sudoku Solver in Python
- PCA-Shift(特征点篇)
- C++模板元编程之使用模板,对数组进行“编译期间求和”
- SAP与ERPv1.0的历史数据迁移简析
- 算法实现计划
- 如何将一个HTML页面嵌套在另一个页面中
- 复制构造函数和类的组合
- Max-Flow Min-Cut
- poj 2395 Out of Hay
- X^a mod b=c 式子中求所有的X(b总是质数)
- RPG中实现类似SQL等值查询和模糊查询