23

While trying to understand how rvalue references work I ended up with this piece of code:

int* iptr = nullptr;
int*&& irr = iptr;

Compiling the above code gives the following error:

error: rvalue reference to type 'int *' cannot bind to lvalue of type 'int *'

I understand this is correct, but why does the following code, where I bind using a void* instead of int*, compiles without any problem? Will the runtime behavior be correct or should I expect undefined behavior?

int* iptr = nullptr;
void*&& irr = iptr;
DeiDei
  • 10,205
  • 6
  • 55
  • 80
Nick
  • 10,309
  • 21
  • 97
  • 201

2 Answers2

22

This is well-formed.

int* and void* are different types; you can't bind a int* to reference to void* directly. The int* needs to be converted to void* firstly, which is a temporary object and could be bound to rvalue-reference. (PS the lifetime of the temporary is extended to the lifetime of the reference.)

Note that irr doesn't bind to iptr; so any modification on it has nothing to do with iptr.

This is not special for void*, same thing happens for other types, e.g.

char c;
int&& r = c; // a temporary int is constructed from c and then bound to r;
             // its lifetime is extened to the lifetime of r
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
11

In addition to @songyuanyao answer: you could make an rvalue out of iptr e.g. by means of static_cast:

  int* iptr = nullptr;
  int*&& irr = static_cast<int *>(iptr);
Igor R.
  • 14,716
  • 2
  • 49
  • 83
  • 1
    Why not just `std::move`? – songyuanyao Apr 20 '18 at 08:26
  • 3
    @songyuanyao because a move would actually bind `irr` to `iptr`. – Quentin Apr 20 '18 at 08:33
  • If you find the `static_cast` clunky, know that `int *&&irr = +iptr;` does the same thing :) – Quentin Apr 20 '18 at 08:34
  • @Quentin Got it. Didn't think the conversion to same type also constructs a new temporary. – songyuanyao Apr 20 '18 at 08:40
  • @songyuanyao consider that for a class type `Foo(anotherFoo)` and `static_cast(anotherFoo)` are equivalent :) – Quentin Apr 20 '18 at 08:50
  • @Quentin Yes I realised that. As you said, it's same as `+iptr` even one is explicit conversion the other is implicit conversion. – songyuanyao Apr 20 '18 at 08:58
  • @Quentin could you expand your comment? what do you mean with actually bind? and what's the difference in those terms if I use the static_cast? – Nick Apr 20 '18 at 12:40
  • 1
    @Nick it means that `irr` refers to (gives access to) `iptr` itself. With the cast, `irr` refers to the unnamed result of the cast (whose lifetime is extended to live as long as `irr`), which is a copy of `iptr`. – Quentin Apr 20 '18 at 12:46
  • @IgorR. [Yes it is](http://en.cppreference.com/w/cpp/language/lifetime#Temporary_object_lifetime): "The lifetime of a temporary object may be extended by binding to a `const` lvalue reference or to an rvalue reference". – Quentin Apr 20 '18 at 13:34
  • 1
    @Quentin You're right. It turns out that the lifespan of *prvalue* is extended in such a case - but not of an *xvalue*. – Igor R. Apr 20 '18 at 13:43