C++ Expression Template 样例解析
2014-04-30 04:06
309 查看
本文解析一个Expression Template的样例,阐述其作用。Expression Template的核心思想在于使用表达式(expression)推迟实际运算,优化执行逻辑,具体细节可在下文例子中体会。
应用场景:
a, b, c是三个Vector类型的变量, 三者相加的结果存于v,即:
v[0] = a[0] + b[0] + c[0];
v[1] = a[1] + b[1] + c[1]; 以此类推。
一般的做法为重载操作符 +,使得其能够进行对于Vector的运算,代码如下:
如此编译时代码将被翻译为如下结构:
这样的程序将有两个问题:
1. 过多的Loop(经历两次长度为length的遍历运算,一次为a+b,另一次为temp + c。)
2. 过多的内存使用 (引入另一临时Vector)
这样的问题虽不影响最终运算结果,但会占用额外的空间与时间。当程序的时间与内存需求严格时(如视频游戏开发),程序的性能将会受到很大影响。
实际上,我们期望的编译结果是这样的:
Expression可以帮助我们获得期望的结果。
代码分为两个个class,分别是Expr,plus,而且操作符 + 和 =被重载,下面分别解释。
---------------------------------------------------------------------------------------
Expr Class 部分
一个Expr class实例表示一个表达式,如a+b对应的表达式为Expr<Vector, plus, Vector>(a,b)是一个表达式。一个表达式由左元素、操作符、右元素三部分组成,如a+b的表达式由a,plus,b组成。(BinOp为Binary Operator的缩写表示一个操作符class类型,如plus,每一个BinOp都会包含一个inline
apply方法做数据的实际操作)
表达式有一个构造函数,这个构造函数使用两个参数分别是表达式的左元素和右元素。
表达式中重载了操作符[],这个重载的作用在于当遇到一个表达式expr 之后紧接[i]情况时调用操作符方法,如Expr<Vector, plus, Vector>(a,b)[0],调用BinOp::apply(a[0],b[0])。
---------------------------------------------------------------------------------------
plus Class部分
plus代表加法运算,含有apply方法进行数据的相加,如plus::apply(3.1, 1.9)会返回5.0.
---------------------------------------------------------------------------------------
操作符+重载
+不在进行真正的加法运算,而是返回一个表达式,如 a + b 返回 Expr<Vector, plus, Vector>(a, b);
---------------------------------------------------------------------------------------
操作符=重载
Vector类型间的等于号被重载,当使用一个Vector类型实例给另一个Vector类型实例赋值时,调用循环从头到尾每个位置上的元素一一赋值。
---------------------------------------------------------------------------------------
我们不妨人工编译一段代码,体验一下整个过程,假设我们需要计算:
应用场景:
int length = 1000; Vector[length] v, a, b, c; v = a + b + c;
a, b, c是三个Vector类型的变量, 三者相加的结果存于v,即:
v[0] = a[0] + b[0] + c[0];
v[1] = a[1] + b[1] + c[1]; 以此类推。
一般的做法为重载操作符 +,使得其能够进行对于Vector的运算,代码如下:
template <class C> C operator + (const C &l, const C &r){ Vector[length] temp; for(int i=0; i < length; i++){ temp[i] = l[i] + r[i]; } return temp; }
如此编译时代码将被翻译为如下结构:
temp = a + b; v = temp + c;
这样的程序将有两个问题:
1. 过多的Loop(经历两次长度为length的遍历运算,一次为a+b,另一次为temp + c。)
2. 过多的内存使用 (引入另一临时Vector)
这样的问题虽不影响最终运算结果,但会占用额外的空间与时间。当程序的时间与内存需求严格时(如视频游戏开发),程序的性能将会受到很大影响。
实际上,我们期望的编译结果是这样的:
for(int i=0; i<length; i++){ v[i] = a[i] + b[i] + c[i]; }
Expression可以帮助我们获得期望的结果。
template <class L, class BinOp, class R> class Expr { L &l_; R &r_; //左元素、右元素 public: Expr(const L &l, const R &r) : l_(l), r_(r) {}; //构造函数(参数初始化表) double operator[](uint i) { //重载运算符[],参数i,针对Expr[i]的情况 return BinOp::apply(l_[i], r_[i]); } }; class plus { inline double apply(double a, double b){ return (a + b); } }; template <class L, class R> Expr<L, plus, R> operator+(const L &l, const R &r) {//重载+,返回Expr return Expr<L, plus, R>(l, r); } template <class Expr> Vector& Vector::operator=(Expr &x) { //重在Vector的= for (int i=0 ; i < this->size(); i++){ (*this)[i] = x[i];//如Vectory v = expr,那么this代表v } return *this; };
代码分为两个个class,分别是Expr,plus,而且操作符 + 和 =被重载,下面分别解释。
---------------------------------------------------------------------------------------
Expr Class 部分
一个Expr class实例表示一个表达式,如a+b对应的表达式为Expr<Vector, plus, Vector>(a,b)是一个表达式。一个表达式由左元素、操作符、右元素三部分组成,如a+b的表达式由a,plus,b组成。(BinOp为Binary Operator的缩写表示一个操作符class类型,如plus,每一个BinOp都会包含一个inline
apply方法做数据的实际操作)
表达式有一个构造函数,这个构造函数使用两个参数分别是表达式的左元素和右元素。
表达式中重载了操作符[],这个重载的作用在于当遇到一个表达式expr 之后紧接[i]情况时调用操作符方法,如Expr<Vector, plus, Vector>(a,b)[0],调用BinOp::apply(a[0],b[0])。
---------------------------------------------------------------------------------------
plus Class部分
plus代表加法运算,含有apply方法进行数据的相加,如plus::apply(3.1, 1.9)会返回5.0.
---------------------------------------------------------------------------------------
操作符+重载
+不在进行真正的加法运算,而是返回一个表达式,如 a + b 返回 Expr<Vector, plus, Vector>(a, b);
---------------------------------------------------------------------------------------
操作符=重载
Vector类型间的等于号被重载,当使用一个Vector类型实例给另一个Vector类型实例赋值时,调用循环从头到尾每个位置上的元素一一赋值。
---------------------------------------------------------------------------------------
我们不妨人工编译一段代码,体验一下整个过程,假设我们需要计算:
v = a + b + c;第一步(第一个加号运算):
v = Expr<Vector,plus,Vector>(a,b) + c;第二步(第二个加号运算):
v = Expr<Expr,plus,Vector>(<Vector,plus,Vector>(a,b), c);第三步(等号运算):
for (int i=0; i<length; i++){ v[i] = Expr<Expr,plus,Vector>(<Vector,plus,Vector>(a,b), c)[i]; }第四步(外层表达式[]运算):
for (int i=0; i<length; i++){ v[i] = plus::apply(<Vector,plus,Vector>(a,b)[i], c[i]); }第五步(内层表达式[]运算):
for (int i=0; i<length; i++){ v[i] = plus::apply(plus::apply(a[i],b[i]), c[i]); }第六步(外层plus::apply方法运算):
for (int i=0; i<length; i++){ v[i] = plus::apply(a[i],b[i]) + c[i]; }第七步(内层plus::apply方法运算):
for (int i=0; i<length; i++){ v[i] = a[i] + b[i] + c[i]; }可以看到我们需要的运算在第七步已经达成,这就是Expression Templates的效果。
相关文章推荐
- 有关C++模板(template)的编译错误“error LNK2019: 无法解析的外部符号”的分析
- 有关C++模板(template)的编译错误“error LNK2019: 无法解析的外部符号”的分析
- 有关C++模板(template)的编译错误“error LNK2019: 无法解析的外部符号”的分析
- c++中template的是用于解析
- 解析C++虚函数表
- C++ 虚函数表解析
- c++ 析构函数调用解析
- C++:关于template与friend联合出现的问题
- C++解析XML-TinyXml
- C/C++宏定义的可变参数详细解析
- 从D语言看C++ Template语法
- java与C++之间进行SOCKET通讯要点简要解析
- c++:template使用中的常见报错
- C++多态的实现及原理详细解析
- C++ 虚函数表解析小结
- C++ #pragma pack()解析
- 2007年9全国计算机等级考试二级笔试试卷C++语言程序设计答案及解析
- C++的XML解析之TinyXML篇
- C++中 模板Template的使用 (typename)
- org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression