class Test {
public:
int n1;
};
Test func() {
return Test();
}
int main() {
func() = Test();
}
This doesn't make sense to me. How and why is this allowed? Is it undefined behavior? If a function returns an rvalue, then how is it possible to set an rvalue to another rvalue? If I tried this with any primitive types, it would give me an error like I expect.
I know that lvalues are a place in memory, so is the function creating a temporary lvalue (rvalue?) and assigning it to another lvalue? Can someone explain how this syntax works?
The value category of the function call expression is in fact an rvalue.
Indeed, you may not call the copy assignment operator on rvalue primitives. The historical definition of rvalues in C was in fact the distinction that they may not be on the left side of an assigment.
The assignment operators of classes are a bit different, though. They're regular member functions. And no rule prevents calling member functions of rvalues. Indeed, it's often quite useful when the functions have side-effects.
How it works, well, the copy assignment operator is called on the temporary, the operator copies the right hand argument, changing the state of the temporary. After the statement, the temporary object is discarded. There is no UB, just pointless copying.
You can prevent calling copy assignment on an rvalue, by declaring the operator with a reference qualifier like this: Test& operator=(Test) & = default;
. Ref-qualifiers were added only later in c++11, so (implicit) copy assignment couldn't have been specified to be ref-qualified earlier. Presumably, C++11 didn't change the qualifier of implicit copy constructor to prevent breaking old code that does assign to an rvalue, even though such assignment does seem quite pointless. The High Integrity C++ Coding Standard recommends that you do use ref qualifier with user defined copy assignment operators.
There's a pretty steep learning curve as far as value categories are concerned (at least to me), but I believe you have got the terminology right on your example. So
func()
indeed returns a prvalue and from the C++ Standard par. 3.10.5 (I only have the current draft, your paragraph number may vary) we read :
An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an
object (9.3) can modify the object. — end example]
So the assignment operator, which is a member function, as in the example mentioned in the standard is an exception to the rule, allowing rvalues to be modified.
This has spawned much criticism in the programming world, with its most extreme example this C++ FQA excerpt :
(Yes, sugar-coated death traps, you clueless cheerleaders. X& obj=a.b().c()
– oops, b()
is a temporary object and c() returns a reference into it! Shouldn't have assigned that to a reference. Not many chances for a compiler warning, either.)
But in real C++ programming it has industrial applications like the named parameter idiom :
std::cout << X::create().setA(10).setB('Z') << std::endl;