Let\'s look at one particular benefit of expression templates: ETs can be used to avoid vector-sized temporaries in memory which occur in overloaded operators like:
Here is the corrected version of Paul Preney's code. I have informed the author by email and in comments; I have written an edit, but it has been rejected by unqualified reviewers.
The error in the original code is that BinaryOp template parameter of math_vector_expr::operator+ is fixed.
#include
#include
#include
#include
#include
//#define DONT_USE_EXPR_TEMPL
//===========================================================================
template class math_vector;
template struct plus_op;
template <
typename LeftExpr,
typename BinaryOp,
typename RightExpr
>
class math_vector_expr
{
public:
typedef typename std::remove_reference::type::value_type value_type;
math_vector_expr() = delete;
math_vector_expr(LeftExpr l, RightExpr r) :
l_(std::forward(l)),
r_(std::forward(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
auto operator +(RE&& re) const ->
math_vector_expr<
math_vector_expr const&,
plus_op,
decltype(std::forward(re))
>
{
return
math_vector_expr<
math_vector_expr const&,
plus_op,
decltype(std::forward(re))
>(*this, std::forward(re))
;
}
auto le() ->
typename std::add_lvalue_reference::type
{ return l_; }
auto le() const ->
typename std::add_lvalue_reference<
typename std::add_const::type
>::type
{ return l_; }
auto re() ->
typename std::add_lvalue_reference::type
{ return r_; }
auto re() const ->
typename std::add_lvalue_reference<
typename std::add_const::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
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
class math_vector
{
using impl_type = std::array;
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 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 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
math_vector(math_vector_expr&& mve)
{
for (std::size_t i = 0; i < N; ++i)
v_[i] = mve[i];
std::cout << this << ": math_vector(expr: " << &mve << ")" << std::endl;
}
template
math_vector& operator =(RightExpr&& re)
{
for (std::size_t i = 0; i
math_vector& operator +=(RightExpr&& re)
{
for (std::size_t i = 0; i
auto operator +(RightExpr&& re) const ->
math_vector_expr<
math_vector const&,
plus_op,
decltype(std::forward(re))
>
{
return
math_vector_expr<
math_vector const&,
plus_op,
decltype(std::forward(re))
>(
*this,
std::forward(re)
)
;
}
#endif // #ifndef DONT_USE_EXPR_TEMPL
private:
impl_type v_;
};
//===========================================================================
template
inline void swap(math_vector& a, math_vector& b)
{
a.swap(b);
}
//===========================================================================
#ifdef DONT_USE_EXPR_TEMPL
template
inline math_vector operator +(
math_vector const& a,
math_vector const& b
)
{
math_vector retval(a);
retval += b;
return retval;
}
template
inline math_vector operator +(
math_vector&& a,
math_vector const& b
)
{
a += b;
return std::move(a);
}
template
inline math_vector operator +(
math_vector const& a,
math_vector&& b
)
{
b += a;
return std::move(b);
}
template
inline math_vector operator +(
math_vector&& a,
math_vector&& b
)
{
a += std::move(b);
return std::move(a);
}
#endif // #ifdef DONT_USE_EXPR_TEMPL
//===========================================================================
template
std::ostream& operator <<(std::ostream& os, math_vector 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;
}
}
//===========================================================================