I was wondering in what kind of cases it makes sense to use move semantics when overloading operator+ and/or operator+=. Even though it is explained in this question how one could do this, I can't wrap my head around as to why I'd do it. Let's consider operator+=. If I just pass right hand side by reference and make the appropriate changes on the left hand side object, there are no unnecessary copies anyway. So we come back to the same point: Would move semantics be beneficial in such a case?
Yes and no.
operator+=
Move semantics are not necessarily helpful for operator+=
in general, because you are already modifying the left hand side argument (this
), so you already have resources to work with most of the times.
Still, as an optimization, it might be worth it. Imagine a std::string
implementation whose default constructor does not allocate any memory. Then std::string::operator+=(std::string&&)
could simply steal the resources from RHS. Or imagine that the RHS buffer is big enough to hold everything but the LHS is not, then if you can use the RHS buffer you're golden: just swap and prepend.
So, it may be worth it, but you have to study it. Therefore:
T& T::operator+=(T const&)
: always presentT& T::operator+=(T&&)
: to enable move semantics when it makes senseoperator+
Here it is always useful (providing we are talking about classes for which move semantics are useful).
The thing is, operator+
produces a temporary (out of the blue) so it generally has to create resources for this temporary. However if it can steal them rather than create them, it's certainly cheaper.
However, you need not provide all overloads:
T operator+(T const&, T const&)
T operator+(T&&, T const&)
T operator+(T const&, T&&)
T operator+(T&&, T&&)
(required for disambiguation)No, you can reuse the same trick that operator=
use and create the temporary right in the function signature (by taking one argument by copy). If the type is movable, the move constructor will get called, otherwise it'll be the copy constructor, but since you need the temporary anyway, no loss of performance.
inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(T const& left, T right) { right += left; return right; } // commutative
inline T operator+(T left, T&& right) { left += right; return left; } // disambiguation
Not much of a gain (3 instead of 4) but well, I'll take what I can!
Of course, for string, operator+
is not commutative (which is why it is a bad overload), so the actual implementation of the second overload would require a prepend
method.
EDIT: following Move semantics and operator overloading it seems that I was a bit over-enthusiastic. Stealing from Ben Voigt's answer, we get:
inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(const T& left, T&& right) { right += left; return right; }
On the other hand, this seems to only work for commutative operations; -
does not work that way but can probably be adapted, /
and %
on the other hand...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With