Rvalue to lvalue conversion?
Asked Answered
M

3

21

Why it is not possible to convert rvalues to lvalues? It is possible to do a conversion in the opposite direction though. Technically rvalues do have a memory address, isn't it?

Moccasin answered 21/6, 2017 at 13:39 Comment(10)
Thought experiment: int x = 1; -- 1 is an rvalue here. Does the literal 1 have a memory address? Can you do &1 to obtain its address?Sensillum
Pass an rvalue into a function. Seriously though the rvalue would cease to exist at the end of the expression where it's access was obtained, leaving you with a dangling pointer.Rosenblum
@Sensillum why is const int& bon = 2; possible then?Moccasin
@OfT Because temporaries are allowed to bind to const references, which extends the lifetime of the temporary. That doesn't change the fact that 2 is an rvalue. My comment is meant to make you think twice about your pondering "technically, rvalues do have a memory address, right?" The answer is that not all rvalues have a memory address.Sensillum
@Sensillum Ahh then it makes sense why it is not allowed to do the conversion, thanks man! But you said "Because temporaries are allowed to bind to const references", but what is bon then referring to, address-wise?Moccasin
@OfT The temporary has the same storage duration as the reference. Typically this is going to be implemented as a stack allocation to hold the temporary (if this line of code appears in a function) and the reference becomes a compile-time detail that is erased. So the reference is referring to the lifetime-extended temporary, and at that point you can take the address. Note that this is different from saying that "2 has an address." Rather, the rvalue 2 was used to construct a temporary int object -- this temporary has an obtainable address only after it is bound to a reference.Sensillum
Wow nice explanation, thanks again! So just to confirm, when doing const int& bon = 2;, the compiler creates a temporary integer that is assigned the given literal at the right-hand-side of the assignment statement. This temporary, in opposite to the literal, has an address that assigned to the const reference anddd the temporary then has a life time equal to the reference. Did I get it right? I really appreciate your time bud, :)) @SensillumMoccasin
@OfT That's correct. The trick is that you can't take the address of something without a name (an rvalue). But by binding that temporary to a reference, you've given it a name (recall that a reference is just another name for an object) and so you're allowed to take its address.Sensillum
@Sensillum You can't do &*"hello"?Psycholinguistics
As curiousguy points out, it's not whether it has a name, it's whether it has a location.Laudian
I
17

You can:

int&& x = 3;

x is now an lvalue. A so called 'rvalue-reference' can bind to a temporary, but anything with a name is an lvalue, so you need to forward<>() it if you need it's rvalueness back.

Note that by binding a temporary to a rvalue-reference (or a const reference) you extend its lifetime. Technically a cast is possible, but not recommended, since temporaries have short lifetime, so you typically get a dangling reference.

Incogitable answered 4/10, 2018 at 11:54 Comment(2)
I don't understand what you mean with the forward<>().Balkin
Can we pass x to a function, func(x); after that?Balkin
F
12

It is rather straightforward to write a template function unmove(), that does the opposite of std::move():

template<class T> T& unmove(T&& t) { return static_cast<T&>(t); }

Please note that, according to the standard since C++11:

a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.

So it is safe to use unmove() within a single full expression, but after the expression has been fully evaluated, the temporaries go away.

My common use for unmove() is to call functions / methods, that return values through references, when I don't need those values.

Femininity answered 12/4, 2021 at 13:29 Comment(2)
Does anyone know if this will still work in c++23? regarding open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2266r3.htmlIncogitable
@Incogitable Yes it breaks due to that addendum. The paper even lists a template from the OpenOffice temporary library o3tl, that has the same purpose and same code as unmove, except, that it is called temporary there. The solution is simple to add an explicit static_cast. Thanks for the pointer and I have already edited the answer.Femininity
R
-1

Correct an anwer above.

int&& x = 3;

x is 'rvalue-reference'. See https://www.tutorialspoint.com/What-is-double-address-operator-and-and-in-Cplusplus

Rizo answered 15/6, 2022 at 19:12 Comment(2)
Both of you are right. "rvalue-reference" is part of the type of x, and "lvalue" is the value-category of x. There is no such value-category as "rvalue-reference".Laudian
The old answer is correct. Expression x is an lvalue of type int, while variable x has type int &&, aka "rvalue reference to int" (not an rvalue, since only expressions can be rvalues/lvalues/etc).Cataplasm

© 2022 - 2024 — McMap. All rights reserved.