expression式模板和C ++ 11

让我们来看看expression式模板的一个特别的好处:可以使用ET来避免在重载操作符中出现的内存中的vector大小的临时对象,如:

template<typename T> std::vector<T> operator+(const std::vector<T>& a, const std::vector<T>& b) { std::vector<T> tmp; // vector-sized temporary for_each(...); return tmp; } 

在C ++ 11中,此函数的返回语句应用移动语义。 vector的副本。 这是一个胜利。

但是,如果我看一下像

 d = a + b + c; 

我看到上面的函数被调用了两次(对于operator+ ),而最后的赋值可以通过移动语义完成。

总共执行2个循环。 意思是我暂时放了一下,然后马上读回来。 对于大型vector而言,这会超出caching。 这比expression式模板更糟糕。 他们可以在一个循环中完成整个事情。 ET可以执行上面的代码相当于:

 for(int i=0 ; i < vec_length ; ++i) d[i] = a[i] + b[i] + c[i]; 

我想知道lambda与移动语义或者其他新特性是否可以和ET一样好。 有什么想法吗?

编辑:

基本上,使用ET技术,编译器会build立一个类似系统的代数expression式的parsing树。 该树由内部节点和叶节点组成。 内部节点表示操作(加法,乘法等),叶节点表示对数据对象的引用。

我试图用堆栈机器的方式来考虑整个计算过程:从操作堆栈中取出一个操作,并从参数堆栈中取出下一个参数并评估操作。 把结果放回堆栈等待操作。

为了表示这两个不同的对象(操作堆栈和数据叶堆栈),我将操作的std::tuple和数据的std::tuple绑定在一起,形成一个std::pair<> 。 最初我使用了std:vector但是这导致了运行时开销。

整个过程分两个阶段:初始化操作和参数堆栈的堆栈初始化。 并且通过将配对的容器分配给向量来触发评估阶段。

我创build了一个Vec类,它包含一个私有array<int,5> (有效载荷),并具有一个重载的赋值运算符,它接受“expression式”。

全局operator*在超过所有组合Vec和“expression式”的情况下被重载,以便在不止a*b的情况下也能正确处理。 (注意,我把这个教育例子转换成乘法 – 基本上是为了迅速发现汇编器中的imull 。)

在评估开始之前,首先完成的是从所涉及的Vec对象中“提取”数值并初始化参数堆栈。 这是没有不同types的对象周围的必要条件:可索引向量和不可索引的结果。 这是Extractor的用途。 再好的事情:使用variables模板,在这种情况下,没有运行时间开销(所有这些都在编译时完成)。

整个事情的作品。 该expression式很好地评估(我也加了添加,但这是留在这里,以适应代码)。 在下面你可以看到汇编输出。 只是原始的计算,就像你想要的一样:与ET技术相提并论。

结果。 C ++ 11的新语言特性提供了可变模板(随着模板元编程)开辟了编译时计算的范围。 我在这里展示了如何使用可变参数模板的优点来生成与传统的ET技术一样好的代码。

 #include<algorithm> #include<iostream> #include<vector> #include<tuple> #include<utility> #include<array> template<typename Target,typename Tuple, int N, bool end> struct Extractor { template < typename ... Args > static Target index(int i,const Tuple& t, Args && ... args) { return Extractor<Target, Tuple, N+1, std::tuple_size<Tuple>::value == N+1>:: index(i, t , std::forward<Args>(args)..., std::get<N>(t).vec[i] ); } }; template < typename Target, typename Tuple, int N > struct Extractor<Target,Tuple,N,true> { template < typename ... Args > static Target index(int i,Tuple const& t, Args && ... args) { return Target(std::forward<Args>(args)...); } }; template < typename ... Vs > std::tuple<typename std::remove_reference<Vs>::type::type_t...> extract(int i , const std::tuple<Vs...>& tpl) { return Extractor<std::tuple<typename std::remove_reference<Vs>::type::type_t...>, std::tuple<Vs...>, 0, std::tuple_size<std::tuple<Vs...> >::value == 0>::index(i,tpl); } struct Vec { std::array<int,5> vec; typedef int type_t; template<typename... OPs,typename... VALs> Vec& operator=(const std::pair< std::tuple<VALs...> , std::tuple<OPs...> >& e) { for( int i = 0 ; i < vec.size() ; ++i ) { vec[i] = eval( extract(i,e.first) , e.second ); } } }; template<int OpPos,int ValPos, bool end> struct StackMachine { template<typename... OPs,typename... VALs> static void eval_pos( std::tuple<VALs...>& vals , const std::tuple<OPs...> & ops ) { std::get<ValPos+1>( vals ) = std::get<OpPos>(ops).apply( std::get<ValPos>( vals ) , std::get<ValPos+1>( vals ) ); StackMachine<OpPos+1,ValPos+1,sizeof...(OPs) == OpPos+1>::eval_pos(vals,ops); } }; template<int OpPos,int ValPos> struct StackMachine<OpPos,ValPos,true> { template<typename... OPs,typename... VALs> static void eval_pos( std::tuple<VALs...>& vals , const std::tuple<OPs...> & ops ) {} }; template<typename... OPs,typename... VALs> int eval( const std::tuple<VALs...>& vals , const std::tuple<OPs...> & ops ) { StackMachine<0,0,false>::eval_pos(const_cast<std::tuple<VALs...>&>(vals),ops); return std::get<sizeof...(OPs)>(vals); } struct OpMul { static int apply(const int& lhs,const int& rhs) { return lhs*rhs; } }; std::pair< std::tuple< const Vec&, const Vec& > , std::tuple<OpMul> > operator*(const Vec& lhs,const Vec& rhs) { return std::make_pair( std::tuple< const Vec&, const Vec& >( lhs , rhs ) , std::tuple<OpMul>( OpMul() ) ); } template<typename... OPs,typename... VALs> std::pair< std::tuple< const Vec&, VALs... > , std::tuple<OPs...,OpMul> > operator*(const Vec& lhs,const std::pair< std::tuple< VALs... > , std::tuple<OPs...> >& rhs) { return std::make_pair( std::tuple_cat( rhs.first , std::tuple< const Vec& >(lhs) ) , std::tuple_cat( rhs.second , std::tuple<OpMul>( OpMul() ) ) ); } template<typename... OPs,typename... VALs> std::pair< std::tuple< const Vec&, VALs... > , std::tuple<OPs...,OpMul> > operator*(const std::pair< std::tuple< VALs... > , std::tuple<OPs...> >& lhs, const Vec& rhs) { return std::make_pair( std::tuple_cat( lhs.first , std::tuple< const Vec& >(rhs) ) , std::tuple_cat( lhs.second , std::tuple<OpMul>( OpMul() ) ) ); } int main() { Vec d,c,b,a; for( int i = 0 ; i < d.vec.size() ; ++i ) { a.vec[i] = 10+i; b.vec[i] = 20+i; c.vec[i] = 30+i; d.vec[i] = 0; } d = a * b * c * a; for( int i = 0 ; i < d.vec.size() ; ++i ) std::cout << d.vec[i] << std::endl; } 

g++-4.6 -O3生成的汇编程序(我不得不将一些运行时依赖放入向量初始化中,以便编译器不会在编译时计算整个东西,而且实际上看到的是imull架。

 imull %esi, %edx imull 32(%rsp), %edx imull %edx, %esi movl 68(%rsp), %edx imull %ecx, %edx movl %esi, (%rsp) imull 36(%rsp), %edx imull %ecx, %edx movl 104(%rsp), %ecx movl %edx, 4(%rsp) movl 72(%rsp), %edx imull %ecx, %edx imull 40(%rsp), %edx imull %ecx, %edx movl 108(%rsp), %ecx movl %edx, 8(%rsp) movl 76(%rsp), %edx imull %ecx, %edx imull 44(%rsp), %edx imull %ecx, %edx movl 112(%rsp), %ecx movl %edx, 12(%rsp) movl 80(%rsp), %edx imull %ecx, %edx imull %eax, %edx imull %ecx, %edx movl %edx, 16(%rsp) 

我想知道lambda与移动语义或者其他新特性是否可以和ET一样好。 有什么想法吗?

快速回答

移动语义本身并不是一个灵丹妙药 – 在C ++ 11中仍然需要诸如expression式模板(ETs)之类的技术来消除周围的数据移动等开销! 因此,在深入解答我的答案之前,快速回答您的问题,移动语义等不会完全取代ET,因为我的答案如下所示。

详细的答案

ET通常会返回代理对象以推迟评估,直到后来,所以在触发计算的代码之前,没有C ++ 11语言function的明显好处。 也就是说,人们不希望编写ET代码,但是,在代理构buildexpression式树期间触发运行时代码生成。 很好,C ++ 11的移动语义和完美的转发可以帮助避免这种情况发生。 (这在C ++ 03中是不可能的。)

实质上,在编写ET时,一旦想调用涉及的代理对象的成员函数,就要利用语言特征来产生最优代码。 在C ++ 11中,这将包括使用完美的转发,通过复制移动语义等,如果实际上仍然需要超过编译器已经可以完成的工作。 游戏的名字是最小化生成的运行时间代码和/或最大化运行时间速度和/或最小化运行时间开销。

我想实际尝试一些使用C ++ 11function的ET来查看是否可以使用a = b + c + d;来删除所有中间临时实例typesa = b + c + d; expression。 (因为这只是我正常活动的一个有趣的rest,所以我没有将它与纯粹使用C ++ 03的ET代码进行比较,也不担心代码抛光的所有方面。

首先,我没有使用lambdaexpression式 – 因为我更喜欢使用显式的types和函数 – 所以我不会为你的问题争论/反对lambdaexpression式。 我的猜测是,他们将类似于使用函数,并且执行比下面的非ET代码更好(即,需要移动) – 至less编译器可以使用自己的内部ET来自动优化lambda。 然而,我写的代码利用了移动语义和完美的转发。 这是我从结果开始,然后最终呈现的代码。

我创build了一个math_vector<N>类,其中N==3 ,它定义了std::array<long double, N>的内部私有实例。 成员是默认的构造函数,复制和移动构造函数和赋值,初始化程序列表构造函数,析构函数,swap()成员,operator []来访问vector和operator + =的元素。 使用没有任何expression式模板,这个代码:

 { cout << "CASE 1:\n"; math_vector<3> a{1.0, 1.1, 1.2}; math_vector<3> b{2.0, 2.1, 2.2}; math_vector<3> c{3.0, 3.1, 3.2}; math_vector<3> d{4.0, 4.1, 4.2}; math_vector<3> result = a + b + c + d; cout << '[' << &result << "]: " << result << "\n"; } 

输出(当用clang++ 3.1或g++ 4.8用 – std=c++11 -O3 ):

 CASE 1: 0x7fff8d6edf50: math_vector(initlist) 0x7fff8d6edef0: math_vector(initlist) 0x7fff8d6ede90: math_vector(initlist) 0x7fff8d6ede30: math_vector(initlist) 0x7fff8d6edd70: math_vector(copy: 0x7fff8d6edf50) 0x7fff8d6edda0: math_vector(move: 0x7fff8d6edd70) 0x7fff8d6eddd0: math_vector(move: 0x7fff8d6edda0) 0x7fff8d6edda0: ~math_vector() 0x7fff8d6edd70: ~math_vector() [0x7fff8d6eddd0]: (10,10.4,10.8) 0x7fff8d6eddd0: ~math_vector() 0x7fff8d6ede30: ~math_vector() 0x7fff8d6ede90: ~math_vector() 0x7fff8d6edef0: ~math_vector() 0x7fff8d6edf50: ~math_vector() 

即使用初始化列表(即初始化项), resultvariables(即0x7fff8d6eddd0 )的四个显式构造实例,并且还使另外三个对象复制和移动。

为了只关注临时和移动,我创build了第二个只创buildresult作为命名variables的例子 – 其他的都是rvalues:

 { cout << "CASE 2:\n"; math_vector<3> result = math_vector<3>{1.0, 1.1, 1.2} + math_vector<3>{2.0, 2.1, 2.2} + math_vector<3>{3.0, 3.1, 3.2} + math_vector<3>{4.0, 4.1, 4.2} ; cout << '[' << &result << "]: " << result << "\n"; } 

输出这个(当不使用ET时):

 CASE 2: 0x7fff8d6edcb0: math_vector(initlist) 0x7fff8d6edc50: math_vector(initlist) 0x7fff8d6edce0: math_vector(move: 0x7fff8d6edcb0) 0x7fff8d6edbf0: math_vector(initlist) 0x7fff8d6edd10: math_vector(move: 0x7fff8d6edce0) 0x7fff8d6edb90: math_vector(initlist) 0x7fff8d6edd40: math_vector(move: 0x7fff8d6edd10) 0x7fff8d6edb90: ~math_vector() 0x7fff8d6edd10: ~math_vector() 0x7fff8d6edbf0: ~math_vector() 0x7fff8d6edce0: ~math_vector() 0x7fff8d6edc50: ~math_vector() 0x7fff8d6edcb0: ~math_vector() [0x7fff8d6edd40]: (10,10.4,10.8) 0x7fff8d6edd40: ~math_vector() 

哪个更好:只创build额外的移动对象。

但我想要更好:我想要零额外的临时性,并有代码,如果我用一个正常的编码警告硬编码的代码:所有明确实例化的types仍然会被创build(即四个initlist构造函数和result )。 为了做到这一点,我添加了expression式模板代码如下:

  1. 创build代理math_vector_expr<LeftExpr,BinaryOp,RightExpr>类来保存尚未计算的expression式,
  2. 创build了一个代理plus_op类来保存添加操作,
  3. 一个构造函数被添加到math_vector来接受一个math_vector_expr对象,
  4. 添加了“starter”成员函数来触发expression式模板的创build。

使用ET的结果是美好的:在这两种情况下都不需要额外的临时对象! 以上两个例子现在输出:

 CASE 1: 0x7fffe7180c60: math_vector(initlist) 0x7fffe7180c90: math_vector(initlist) 0x7fffe7180cc0: math_vector(initlist) 0x7fffe7180cf0: math_vector(initlist) 0x7fffe7180d20: math_vector(expr: 0x7fffe7180d90) [0x7fffe7180d20]: (10,10.4,10.8) 0x7fffe7180d20: ~math_vector() 0x7fffe7180cf0: ~math_vector() 0x7fffe7180cc0: ~math_vector() 0x7fffe7180c90: ~math_vector() 0x7fffe7180c60: ~math_vector() CASE 2: 0x7fffe7180dd0: math_vector(initlist) 0x7fffe7180e20: math_vector(initlist) 0x7fffe7180e70: math_vector(initlist) 0x7fffe7180eb0: math_vector(initlist) 0x7fffe7180d20: math_vector(expr: 0x7fffe7180dc0) 0x7fffe7180eb0: ~math_vector() 0x7fffe7180e70: ~math_vector() 0x7fffe7180e20: ~math_vector() 0x7fffe7180dd0: ~math_vector() [0x7fffe7180d20]: (10,10.4,10.8) 0x7fffe7180d20: ~math_vector() 

即每种情况下只有5个构造函数调用和5个析构函数调用。 实际上,如果您要求编译器在4个initlist构造函数调用之间生成汇编代码,并输出result initlist得到如下美丽的汇编代码:

 fldt 128(%rsp) leaq 128(%rsp), %rdi leaq 80(%rsp), %rbp fldt 176(%rsp) faddp %st, %st(1) fldt 224(%rsp) faddp %st, %st(1) fldt 272(%rsp) faddp %st, %st(1) fstpt 80(%rsp) fldt 144(%rsp) fldt 192(%rsp) faddp %st, %st(1) fldt 240(%rsp) faddp %st, %st(1) fldt 288(%rsp) faddp %st, %st(1) fstpt 96(%rsp) fldt 160(%rsp) fldt 208(%rsp) faddp %st, %st(1) fldt 256(%rsp) faddp %st, %st(1) fldt 304(%rsp) faddp %st, %st(1) fstpt 112(%rsp) 

g++clang++输出相似(甚至更小)的代码。 没有函数调用等 – 只是一堆补充,这是完全是人们想要的!

下面是实现这个的C ++ 11代码。 只要#define DONT_USE_EXPR_TEMPL不要使用ET,或根本不定义它使用ET。

 #include <array> #include <algorithm> #include <initializer_list> #include <type_traits> #include <iostream> //#define DONT_USE_EXPR_TEMPL //=========================================================================== template <std::size_t N> class math_vector; template < typename LeftExpr, typename BinaryOp, typename RightExpr > class math_vector_expr { public: math_vector_expr() = delete; math_vector_expr(LeftExpr l, RightExpr r) : l_(std::forward<LeftExpr>(l)), r_(std::forward<RightExpr>(r)) { } // Prohibit copying... math_vector_expr(math_vector_expr const&) = delete; math_vector_expr& operator =(math_vector_expr const&) = delete; // Allow moves... math_vector_expr(math_vector_expr&&) = default; math_vector_expr& operator =(math_vector_expr&&) = default; template <typename RE> auto operator +(RE&& re) const -> math_vector_expr< math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, BinaryOp, decltype(std::forward<RE>(re)) > { return math_vector_expr< math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, BinaryOp, decltype(std::forward<RE>(re)) >(*this, std::forward<RE>(re)) ; } auto le() -> typename std::add_lvalue_reference<LeftExpr>::type { return l_; } auto le() const -> typename std::add_lvalue_reference< typename std::add_const<LeftExpr>::type >::type { return l_; } auto re() -> typename std::add_lvalue_reference<RightExpr>::type { return r_; } auto re() const -> typename std::add_lvalue_reference< typename std::add_const<RightExpr>::type >::type { return r_; } auto operator [](std::size_t index) const -> decltype( BinaryOp::apply(this->le()[index], this->re()[index]) ) { return BinaryOp::apply(le()[index], re()[index]); } private: LeftExpr l_; RightExpr r_; }; //=========================================================================== template <typename T> struct plus_op { static T apply(T const& a, T const& b) { return a + b; } static T apply(T&& a, T const& b) { a += b; return std::move(a); } static T apply(T const& a, T&& b) { b += a; return std::move(b); } static T apply(T&& a, T&& b) { a += b; return std::move(a); } }; //=========================================================================== template <std::size_t N> class math_vector { using impl_type = std::array<long double, N>; public: math_vector() { using namespace std; fill(begin(v_), end(v_), impl_type{}); std::cout << this << ": math_vector()" << endl; } math_vector(math_vector const& mv) noexcept { using namespace std; copy(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector(copy: " << &mv << ")" << endl; } math_vector(math_vector&& mv) noexcept { using namespace std; move(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector(move: " << &mv << ")" << endl; } math_vector(std::initializer_list<typename impl_type::value_type> l) { using namespace std; copy(begin(l), end(l), begin(v_)); std::cout << this << ": math_vector(initlist)" << endl; } math_vector& operator =(math_vector const& mv) noexcept { using namespace std; copy(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector op =(copy: " << &mv << ")" << endl; return *this; } math_vector& operator =(math_vector&& mv) noexcept { using namespace std; move(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector op =(move: " << &mv << ")" << endl; return *this; } ~math_vector() { using namespace std; std::cout << this << ": ~math_vector()" << endl; } void swap(math_vector& mv) { using namespace std; for (std::size_t i = 0; i<N; ++i) swap(v_[i], mv[i]); } auto operator [](std::size_t index) const -> typename impl_type::value_type const& { return v_[index]; } auto operator [](std::size_t index) -> typename impl_type::value_type& { return v_[index]; } math_vector& operator +=(math_vector const& b) { for (std::size_t i = 0; i<N; ++i) v_[i] += b[i]; return *this; } #ifndef DONT_USE_EXPR_TEMPL template <typename LE, typename Op, typename RE> math_vector(math_vector_expr<LE,Op,RE>&& mve) { for (std::size_t i = 0; i < N; ++i) v_[i] = mve[i]; std::cout << this << ": math_vector(expr: " << &mve << ")" << std::endl; } template <typename RightExpr> math_vector& operator =(RightExpr&& re) { for (std::size_t i = 0; i<N; ++i) v_[i] = re[i]; return *this; } template <typename RightExpr> math_vector& operator +=(RightExpr&& re) { for (std::size_t i = 0; i<N; ++i) v_[i] += re[i]; return *this; } template <typename RightExpr> auto operator +(RightExpr&& re) const -> math_vector_expr< math_vector const&, plus_op<typename impl_type::value_type>, decltype(std::forward<RightExpr>(re)) > { return math_vector_expr< math_vector const&, plus_op<typename impl_type::value_type>, decltype(std::forward<RightExpr>(re)) >( *this, std::forward<RightExpr>(re) ) ; } #endif // #ifndef DONT_USE_EXPR_TEMPL private: impl_type v_; }; //=========================================================================== template <std::size_t N> inline void swap(math_vector<N>& a, math_vector<N>& b) { a.swap(b); } //=========================================================================== #ifdef DONT_USE_EXPR_TEMPL template <std::size_t N> inline math_vector<N> operator +( math_vector<N> const& a, math_vector<N> const& b ) { math_vector<N> retval(a); retval += b; return retval; } template <std::size_t N> inline math_vector<N> operator +( math_vector<N>&& a, math_vector<N> const& b ) { a += b; return std::move(a); } template <std::size_t N> inline math_vector<N> operator +( math_vector<N> const& a, math_vector<N>&& b ) { b += a; return std::move(b); } template <std::size_t N> inline math_vector<N> operator +( math_vector<N>&& a, math_vector<N>&& b ) { a += std::move(b); return std::move(a); } #endif // #ifdef DONT_USE_EXPR_TEMPL //=========================================================================== template <std::size_t N> std::ostream& operator <<(std::ostream& os, math_vector<N> const& mv) { os << '('; for (std::size_t i = 0; i < N; ++i) os << mv[i] << ((i+1 != N) ? ',' : ')'); return os; } //=========================================================================== int main() { using namespace std; try { { cout << "CASE 1:\n"; math_vector<3> a{1.0, 1.1, 1.2}; math_vector<3> b{2.0, 2.1, 2.2}; math_vector<3> c{3.0, 3.1, 3.2}; math_vector<3> d{4.0, 4.1, 4.2}; math_vector<3> result = a + b + c + d; cout << '[' << &result << "]: " << result << "\n"; } cout << endl; { cout << "CASE 2:\n"; math_vector<3> result = math_vector<3>{1.0, 1.1, 1.2} + math_vector<3>{2.0, 2.1, 2.2} + math_vector<3>{3.0, 3.1, 3.2} + math_vector<3>{4.0, 4.1, 4.2} ; cout << '[' << &result << "]: " << result << "\n"; } } catch (...) { return 1; } } //=========================================================================== 

这是Paul Preney的代码的正确版本。 我通过电子邮件和评论通知了作者。 我已经写了一个编辑,但却被不合格的审稿人拒绝了。

原始代码中的错误是math_vector_expr :: operator +的BinaryOp模板参数是固定的。

 #include <array> #include <algorithm> #include <initializer_list> #include <type_traits> #include <iostream> //#define DONT_USE_EXPR_TEMPL //=========================================================================== template <std::size_t N> class math_vector; template <typename T> struct plus_op; template < typename LeftExpr, typename BinaryOp, typename RightExpr > class math_vector_expr { public: typedef typename std::remove_reference<LeftExpr>::type::value_type value_type; math_vector_expr() = delete; math_vector_expr(LeftExpr l, RightExpr r) : l_(std::forward<LeftExpr>(l)), r_(std::forward<RightExpr>(r)) { } // Prohibit copying... math_vector_expr(math_vector_expr const&) = delete; math_vector_expr& operator =(math_vector_expr const&) = delete; // Allow moves... math_vector_expr(math_vector_expr&&) = default; math_vector_expr& operator =(math_vector_expr&&) = default; template <typename RE> auto operator +(RE&& re) const -> math_vector_expr< math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, plus_op<value_type>, decltype(std::forward<RE>(re)) > { return math_vector_expr< math_vector_expr<LeftExpr,BinaryOp,RightExpr> const&, plus_op<value_type>, decltype(std::forward<RE>(re)) >(*this, std::forward<RE>(re)) ; } auto le() -> typename std::add_lvalue_reference<LeftExpr>::type { return l_; } auto le() const -> typename std::add_lvalue_reference< typename std::add_const<LeftExpr>::type >::type { return l_; } auto re() -> typename std::add_lvalue_reference<RightExpr>::type { return r_; } auto re() const -> typename std::add_lvalue_reference< typename std::add_const<RightExpr>::type >::type { return r_; } auto operator [](std::size_t index) const -> value_type { return BinaryOp::apply(le()[index], re()[index]); } private: LeftExpr l_; RightExpr r_; }; //=========================================================================== template <typename T> struct plus_op { static T apply(T const& a, T const& b) { return a + b; } static T apply(T&& a, T const& b) { a += b; return std::move(a); } static T apply(T const& a, T&& b) { b += a; return std::move(b); } static T apply(T&& a, T&& b) { a += b; return std::move(a); } }; //=========================================================================== template <std::size_t N> class math_vector { using impl_type = std::array<long double, N>; public: typedef typename impl_type::value_type value_type; math_vector() { using namespace std; fill(begin(v_), end(v_), impl_type{}); std::cout << this << ": math_vector()" << endl; } math_vector(math_vector const& mv) noexcept { using namespace std; copy(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector(copy: " << &mv << ")" << endl; } math_vector(math_vector&& mv) noexcept { using namespace std; move(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector(move: " << &mv << ")" << endl; } math_vector(std::initializer_list<value_type> l) { using namespace std; copy(begin(l), end(l), begin(v_)); std::cout << this << ": math_vector(initlist)" << endl; } math_vector& operator =(math_vector const& mv) noexcept { using namespace std; copy(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector op =(copy: " << &mv << ")" << endl; return *this; } math_vector& operator =(math_vector&& mv) noexcept { using namespace std; move(begin(mv.v_), end(mv.v_), begin(v_)); std::cout << this << ": math_vector op =(move: " << &mv << ")" << endl; return *this; } ~math_vector() { using namespace std; std::cout << this << ": ~math_vector()" << endl; } void swap(math_vector& mv) { using namespace std; for (std::size_t i = 0; i<N; ++i) swap(v_[i], mv[i]); } auto operator [](std::size_t index) const -> value_type const& { return v_[index]; } auto operator [](std::size_t index) -> value_type& { return v_[index]; } math_vector& operator +=(math_vector const& b) { for (std::size_t i = 0; i<N; ++i) v_[i] += b[i]; return *this; } #ifndef DONT_USE_EXPR_TEMPL template <typename LE, typename Op, typename RE> math_vector(math_vector_expr<LE,Op,RE>&& mve) { for (std::size_t i = 0; i < N; ++i) v_[i] = mve[i]; std::cout << this << ": math_vector(expr: " << &mve << ")" << std::endl; } template <typename RightExpr> math_vector& operator =(RightExpr&& re) { for (std::size_t i = 0; i<N; ++i) v_[i] = re[i]; return *this; } template <typename RightExpr> math_vector& operator +=(RightExpr&& re) { for (std::size_t i = 0; i<N; ++i) v_[i] += re[i]; return *this; } template <typename RightExpr> auto operator +(RightExpr&& re) const -> math_vector_expr< math_vector const&, plus_op<value_type>, decltype(std::forward<RightExpr>(re)) > { return math_vector_expr< math_vector const&, plus_op<value_type>, decltype(std::forward<RightExpr>(re)) >( *this, std::forward<RightExpr>(re) ) ; } #endif // #ifndef DONT_USE_EXPR_TEMPL private: impl_type v_; }; //=========================================================================== template <std::size_t N> inline void swap(math_vector<N>& a, math_vector<N>& b) { a.swap(b); } //=========================================================================== #ifdef DONT_USE_EXPR_TEMPL template <std::size_t N> inline math_vector<N> operator +( math_vector<N> const& a, math_vector<N> const& b ) { math_vector<N> retval(a); retval += b; return retval; } template <std::size_t N> inline math_vector<N> operator +( math_vector<N>&& a, math_vector<N> const& b ) { a += b; return std::move(a); } template <std::size_t N> inline math_vector<N> operator +( math_vector<N> const& a, math_vector<N>&& b ) { b += a; return std::move(b); } template <std::size_t N> inline math_vector<N> operator +( math_vector<N>&& a, math_vector<N>&& b ) { a += std::move(b); return std::move(a); } #endif // #ifdef DONT_USE_EXPR_TEMPL //=========================================================================== template <std::size_t N> std::ostream& operator <<(std::ostream& os, math_vector<N> const& mv) { os << '('; for (std::size_t i = 0; i < N; ++i) os << mv[i] << ((i+1 != N) ? ',' : ')'); return os; } //=========================================================================== int main() { using namespace std; try { { cout << "CASE 1:\n"; math_vector<3> a{1.0, 1.1, 1.2}; math_vector<3> b{2.0, 2.1, 2.2}; math_vector<3> c{3.0, 3.1, 3.2}; math_vector<3> d{4.0, 4.1, 4.2}; math_vector<3> result = a + b + c + d; cout << '[' << &result << "]: " << result << "\n"; } cout << endl; { cout << "CASE 2:\n"; math_vector<3> result = math_vector<3>{1.0, 1.1, 1.2} + math_vector<3>{2.0, 2.1, 2.2} + math_vector<3>{3.0, 3.1, 3.2} + math_vector<3>{4.0, 4.1, 4.2} ; cout << '[' << &result << "]: " << result << "\n"; } } catch (...) { return 1; } } //===========================================================================