1

I was tracking down a compilation error when I came to this case:

struct Y        
{               
  int&& y;      
  Y(int&& y)    
    : y(y)      
  {             
  }             
};              

struct ZZ {};   
struct Z        
{               
  ZZ&& z;       
  Z(ZZ&& z)     
    : z(z)      
  {             
  }             
};

These both fail stating:

exec.cpp: In constructor ‘Y::Y(int&&)’:
exec.cpp:57:10: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘int’
exec.cpp: In constructor ‘Z::Z(ZZ&&)’:
exec.cpp:67:10: error: invalid initialization of reference of type ‘ZZ&&’ from expression of type ‘ZZ’

But I'm not exactly sure why. What is wrong here?

I'm using g++4.5.3 with -std=gnu++0x option, but it also occurs with -std=c++0x option.

Adrian
  • 10,246
  • 4
  • 44
  • 110
  • 3
    I'm having a hard time finding a case where storing a rvalue reference as a member is useful. Can you give use a little more detail on the background of your problem ? – Alexandre C. May 25 '13 at 15:17
  • Actually, I'm just trying to get my head around move semantics and what && really means. I'm not sure if there is a reason for it. – Adrian May 25 '13 at 16:55
  • Update: I think that having a r-value reference as a member has almost no value and can be very dangerous, if the object with that member has any significant lifespan. There may be some value, but it would appear to be very small. – Adrian May 25 '13 at 17:11

2 Answers2

2

You need to say : y(std::move(y)). That's the only way to get an expression that can bind to an rvalue reference. Just the naked expression y is an lvalue.

(Note that it's very dangerous and hard to make correct to store reference class members.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Note that while `std::move` is certainly the *normal* way, it's equally certainly not the only way. `std::move` is written in normal C++, and [you can duplicate it yourself](http://stackoverflow.com/a/16745424/179910) at will (at heart, it's little more than a simple static_cast). – Jerry Coffin May 25 '13 at 15:18
  • 1
    @JerryCoffin: Yes, of course. The value of `static_cast(x)`, where `T` is a non-reference type, is an xvalue, and thus can bind to an rvalue reference. I thought the answer was at the right level for the question :-) – Kerrek SB May 25 '13 at 15:20
  • Yeah, I know that when making a member a reference to something, the object's lifespan must be less then the lifespan of the referenced object, otherwise the equivalent of a dangling pointer can result. – Adrian May 25 '13 at 16:51
  • In fact this is more dangerous to have a reference to an r-value than an l-value as a member since an r-value is a temporary and there is no way of guaranteeing its lifespan. – Adrian May 25 '13 at 17:06
2

Anything which has a name is an l-value. This means that the constructor parameter y is a l-value (of type r-value reference to int), because it has a name, "y".

Use std::move(y) to turn it back into a r-value.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Oh, I see. `int & i` means `i` is a reference to a named l-value, and `int && i` means `i` is a reference to a named r-value. Is that right? But I though that a function that takes something like `int && i` could take a l or r value. I'm going to have to do more reading on move semantics.... – Adrian May 25 '13 at 16:48
  • @Adrian `int &i` means `i` is a reference to an l-value of type `int`. `int &&i` means `i` is a reference to an r-value of type `int`. In both cases `i` **itself** is an l-value. And no, r-value references (`&&`) cannot bind to l-values. Also, there is no such thing as a "named r-value." When something has a name, it's an l-value. – Angew is no longer proud of SO May 25 '13 at 17:08
  • @Angew: Both `int &` and `int &&` are *references*. An id-expression of a variable of one of these types is always an `int`. The difference is in what sort of values the reference can bind to. – Kerrek SB May 25 '13 at 17:13
  • @Angew: Sorry, I didn't really mean a named r-value, but that the r-value has been given a name through an r-value reference. But if you have an r-value reference, that implies that it's now an l-value since it has a name? Is that right? – Adrian May 25 '13 at 17:21
  • 1
    @Adrian The r-value referred to is still an r-value. It's the reference variable through which you have access to it that is an l-value. And `std::move()` allows you to refer to it as an r-value because it's an expression, not a name. You can think of `std::move(r)` as returning "the r-value to which `r` refers." – Angew is no longer proud of SO May 25 '13 at 17:47