I'm reading about C++ rvalue references and value categories for expressions. However, I'm not able to grasp (from a lvalue, xvalue, prvalue point of view) why the following code works as expected:
struct S {
int a;
};
S&& t(S& a) {
return (S&&)a;
}
int main()
{
S s = { 1 };
t(s) = S{ 2 };
cout << s.a; // prints 2
return 0;
}
Whereas the following one doesn't even compile, and shows error: using rvalue as lvalue:
int&& t(int& a) {
return (int&&)(a);
}
int main()
{
int s = 1;
t(s) = 2; // error
std::cout << s;
return 0;
}
To me, in both cases t(s) should behave as an xvalue. Hence, it can appear to the left of the assignment operator (not discussing operator overriding in this context). The standard literally says that The result of calling a function whose return type is an rvalue reference is an xvalue. Why is it behaving differently for int and for struct S? Is this behavior, in any way, predicted by the standard (or by cppreference.com)? I couldn't realize what order of ideas gets into this scenario.
I was expecting the second code to print 2, based on the reasoning that the memory location of s, initially holding 1 would be overwritten by 2 by means of the rvalue reference.
As pointed out by the standard:
[Note 1: ...For example, the built-in assignment operators expect that the left operand is an lvalue and that the right operand is a prvalue and yield an lvalue as the result. User-defined operators are functions, and the categories of values they expect and yield are determined by their parameter and return types. — end note]
That is, the operator= used by S is actually a function that's implicitly generated by the compiler. So, the default one is like:
S& operator=(const S&) { /*...*/ return *this; }
S& operator=(S&&){ /*...*/ return *this; }
This will allow the user to call the function, which behaves differently from the built-in operators; however, if you explicitly disable it by reference qualifiers:
S& operator=(const S&) & { /*...*/ return *this; }
S& operator=(S&&) & { /*...*/ return *this; }
// Or e.g. S& operator=(S&&) & =default;
Then the function can only be used for lvalue.
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