(note the original question title had "instead of an rvalue" rather than "instead of a const reference". One of the answers below is in response to the old title. This was fixed for clarity)
One common construct in C and C++ is for chained assignments, e.g.
int j, k;
j = k = 1;
The second =
is performed first, with the expression k=1
having the side effect that k
is set to 1, while the value of the expression itself is 1.
However, one construct that is legal in C++ (but not in C) is the following, which is valid for all base types:
int j, k=2;
(j=k) = 1;
Here, the expression j=k
has the side effect of setting j
to 2, and the expression itself becomes a reference to j
, which then sets j
to 1. As I understand, this is because the expression j=k
returns a non-const
int&
, e.g. generally speaking an lvalue.
This convention is usually also recommended for user-defined types, as explained in "Item 10: Have assignment operators return a (non-const) reference to *this" in Meyers Effective C++(parenthetical addition mine). That section of the book does not attempt to explain why the reference is a non-const
one or even note the non-const
ness in passing.
Of course, this certainly adds functionality, but the statement (j=k) = 1;
seems awkward to say the least.
If the convention were to instead have builtin assignment return const references, then custom classes would also use this convention, and the original chained construction allowed in C would still work, without any extraneous copies or moves. For example, the following runs correctly:
#include <iostream>
using std::cout;
struct X{
int k;
X(int k): k(k){}
const X& operator=(const X& x){
// the first const goes against convention
k = x.k;
return *this;
}
};
int main(){
X x(1), y(2), z(3);
x = y = z;
cout << x.k << '\n'; // prints 3
}
with the advantage being that all 3 (C builtins, C++ builtins, and C++ custom types) all are consistent in not allowing idioms like (j=k) = 1
.
Was the addition of this idiom between C and C++ intentional? And if so, what type of situation would justify its use? In other words, what non-spurious benefit does does this expansion in functionality ever provide?