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?

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 9
    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? – cdhowie Jun 21 '17 at 13:40
  • 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. – Richard Critten Jun 21 '17 at 13:42
  • @cdhowie why is `const int& bon = 2;` possible then? –  Jun 21 '17 at 14:58
  • 4
    @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. – cdhowie Jun 21 '17 at 16:11
  • @cdhowie 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? –  Jun 21 '17 at 16:39
  • @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. – cdhowie Jun 21 '17 at 16:44
  • 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, :)) @cdhowie –  Jun 21 '17 at 17:37
  • 1
    @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. – cdhowie Jun 21 '17 at 20:34
  • 1
    @cdhowie You can't do `&*"hello"`? – curiousguy Feb 05 '19 at 14:28
  • 1
    As curiousguy points out, it's not whether it has a *name*, it's whether it has a *location*. – Ben Voigt Jun 15 '22 at 19:20

3 Answers3

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.

sp2danny
  • 7,488
  • 3
  • 31
  • 53
10

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.

Kai Petzke
  • 2,150
  • 21
  • 29
  • Does anyone know if this will still work in c++23? regarding https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2266r3.html – sp2danny Jul 09 '23 at 10:14
  • 1
    @sp2danny 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. – Kai Petzke Jul 11 '23 at 17:43
0

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

  • 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". – Ben Voigt Jun 15 '22 at 19:17
  • 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). – HolyBlackCat Jun 15 '22 at 19:17