For class types it is possible to assign to temporary objects which is actually not allowed for built-in types. Further, the assignment operator generated by default even yields an lvalue:
int() = int(); // illegal: "expression is not assignable"
struct B {};
B& b = B() = B(); // compiles OK: yields an lvalue! ... but is wrong! (see below)
For the last statement the result of the assignment operator is actually used to initialize a non-const
reference which will become stale immediately after the statement: the reference isn't bound to the temporary object directly (it can't as temporary objects can only be bound to a const
or rvalue references) but to the result of the assignment whose life-time isn't extended.
Another problem is that the lvalue returned from the assignment operator doesn't look as if it can be moved although it actually refers to a temporary. If anything is using the result of the assignment to get hold of the value it will be copied rather than moved although it would be entirely viable to move. At this point it is worth noting that the problem is described in terms of the assignment operator because this operator is typically available for value types and returns an lvalue reference. The same problem exists for any function returning a reference to the objects, i.e., *this
.
A potential fix is to overload the assignment operator (or other functions returning a reference to the object) to consider the kind of object, e.g.:
class G {
public:
// other members
G& operator=(G) & { /*...*/ return *this; }
G operator=(G) && { /*...*/ return std::move(*this); }
};
The possibility to overload the assignment operators as above has come with C++11 and would prevent the subtle object invalidation noted above and simultaneously allow moving the result of an assignment to a temporary. The implementation of the these two operators is probably identical. Although the implementation is likely to be rather simple (essentially just a swap()
of the two objects) it still means extra work raising the question:
Should functions returning a reference to the object (e.g., the assignment operator) observe the rvalueness of the object being assigned to?
An alternatively (mentioned by Simple in a comment) is to not overload the assignment operator but to qualify it explicitly with a &
to restrict its use to lvalues:
class GG {
public:
// other members
GG& operator=(GG) & { /*...*/ return *this; }
};
GG g;
g = GG(); // OK
GG() = GG(); // ERROR
See Question&Answers more detail:os