[SOLVED] when can xvalues become lvalues?

Issue

This Content is from Stack Overflow. Question asked by sinthetiq

or, to phrase it another way; when can we assign to a temporary?

cppreference.com says:
“an rvalue expression is either prvalue or xvalue” and “an rvalue can’t be used as the left-hand operand of the built-in assignment or compound assignment operators”

with that context in mind, I would like to understand what is happening in this code, specifically; why does the last line work, when all the others fail?

struct type { int x; };
type get_type() { return type{0}; }
int get_int() { return int{0}; }
int main() {
    int * p1 = &get_int();// fail "cannot take the address of an rvalue"
    type * p2 = &get_type();// fail "taking the address of a temporary object"
    get_int() = int{1};// fail "expression is not assignable"
    get_type() = type{1};// works
}



Solution

Assuming C++17 or later:

int * p1 = &get_int();// fail "cannot take the address of an rvalue"

A function call expression to a function returning by-value is a prvalue expression. The built-in & does not accept prvalues as operand.

type * p2 = &get_type();// fail "taking the address of a temporary object"

Same as above.

get_int() = int{1};// fail "expression is not assignable"

As above get_int() is a prvalue. Overload resolution will choose again the built-in = here, which does not accept prvalues on the left-hand side.

get_type() = type{1};// works

get_type() is still a prvalue and type{1} is (the same as int{1}) a functional style explicit cast to a non-reference type, which are also prvalue expressions. However overload resolution will not choose the built-in =, but instead the implicit (move) assignment operator of type which is declared as

constexpr type& operator=(type&&) noexcept;

is chosen, so that we have effectively a member function call:

get_type().operator=(type{1})

This causes both prvalues to be converted to xvalues by temporary materialization. This is triggered by calling a member function on the prvalue or by initializing a reference with the prvalue (the one in the parameter). The reference is then bound to the temporary object.

For a (non-normative) list of situations in which temporary materialization (i.e. prvalue-to-xvalue conversion) happens see the note 3 in [class.temporary]. When the conversion is applied is described normatively throughout the standard at relevant places. Cppreference also has a list of these situations.

(The compiler error messages are not using precise standard terminology by the way. That is often the case. For example in the first two cases no temporary is ever created. Taking the address of a temporary is therefore not really the cause of the failure. Instead the problem is just what I described above.)


Practically speaking, you can not assign to non-class-type rvalues, but you usually can to class-type rvalues. That is how the implicit assignment operator works and how the conventional assignment operators work. However a class can be defined so that it accepts assignment only to lvalues.


Also, xvalues do not become lvalues. The relevant part is only that prvalues become xvalues by temporary materialization conversion and that both xvalues and lvalues (collectively glvalues) share a lot of behavior, in particular that they refer to objects or functions (which prvalues don’t).


This Question was asked in StackOverflow by sinthetiq and Answered by user17732522 It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?