I am coding a small numeric analysis library in C++. I have been trying to implement using the latest C++11 features including move semantics. I understand the discussion and top answer at the following post: C++11 rvalues and move semantics confusion (return statement) , but there is one scenario that I still am trying to wrap my head around.
I have a class, call it T
, which is fully equipped with overloaded operators. I also have both copy and move constructors.
T (const T &) { /*initialization via copy*/; }
T (T &&) { /*initialization via move*/; }
My client code heavily uses operators, so I am trying to ensure that complex arithmetic expressions get maximum benefit from move semantics. Consider the following:
T a, b, c, d, e;
T f = a + b * c - d / e;
Without move semantics, my operators are making a new local variable using the copy constructor each time, so there are a total of 4 copies. I was hoping that with move semantics I could reduce this to 2 copies plus some moves. In the parenthesized version:
T f = a + (b * c) - (d / e);
each of (b * c)
and (d / e)
must create the temporary in the usual way with a copy, but then it would be great if I could leverage one of those temporaries to accumulate the remaining results with only moves.
Using g++ compiler, I have been able to do this, but I suspect my technique may not be safe and I want to fully understand why.
Here is an example implementation for the addition operator:
T operator+ (T const& x) const
{
T result(*this);
// logic to perform addition here using result as the target
return std::move(result);
}
T operator+ (T&& x) const
{
// logic to perform addition here using x as the target
return std::move(x);
}
Without the calls to std::move
, then only the const &
version of each operator is ever invoked. But when using std::move
as above, subsequent arithmetic (after the innermost expressions) are performed using the &&
version of each operator.
I know that RVO can be inhibited, but on very computationally-expensive, real-world problems it seems that the gain slightly outweighs the lack of RVO. That is, over millions of computations I do get a very tiny speedup when I include std::move
. Though in all honesty it is fast enough without. I really just want to fully comprehend the semantics here.
Is there a kind C++ Guru who is willing to take the time to explain, in a simple way, whether and why my use of std::move is a bad thing here? Many thanks in advance.