您的位置:首页 > 编程语言 > C语言/C++

C++ Expression Template 样例解析

2014-04-30 04:06 309 查看
本文解析一个Expression Template的样例,阐述其作用。Expression Template的核心思想在于使用表达式(expression)推迟实际运算,优化执行逻辑,具体细节可在下文例子中体会。

应用场景:

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,分别是Exprplus,而且操作符 + 和 =被重载,下面分别解释。

---------------------------------------------------------------------------------------

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的效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: