It just occurred to me that operator+
& co can operate on this
for rvalues; i.e. given a class C
, it's possible to do this:
class C {
// ...
C operator-( const C& rhs ) const & {
C result = *this;
result -= rhs;
return result;
}
C&& operator-( const C& rhs ) && {
*this -= rhs;
return std::move( *this );
}
};
This would prevent copies by simply modifying temporary values in-place.
Would this perform as I expect? Is it a reasonable optimization or would the compiler create equally fast code?
Let's say we just wrap std::string
and do a simplified version of operator+
:
struct C {
std::string val;
C&& operator+(const C& rhs) && {
val += rhs.val;
return std::move(*this);
}
std::string::iterator begin() { return val.begin(); }
std::string::iterator end() { return val.end(); }
};
With that, this works fine:
for (char c : C{"hello"}) { .. }
the range-for expression will extend the lifetime of the temporary, so we're ok. However, consider this:
for (char c : C{"hello"} + C{"goodbye"}) { .. }
We effectively have:
auto&& __range = C{"hello"}.operator+(C{"goodbye"});
Here, we're not binding a temporary to a reference. We're binding a reference. The object doesn't get its lifetime extended because... it's not an object. So we have a dangling reference, and undefined behavior. This would be very surprising to users who would expect this to work:
for (char c : std::string{"hello"} + std::string{"goodbye"}) { .. }
You'd have to return a value:
C operator+(const C& rhs) && {
val += rhs.val;
return std::move(*this);
}
That solves this issue (as now we have temporary extension), and if moving your objects is cheaper than copying them, this is a win.