Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: Why is assigning rvalues allowed?

From what I understand the reason why it is dangerous to return rvalues references to from functions is due to the following code:

T&& f(T&& x) { do_something_to_T(x); return static_cast<T&&>(x); }
T f(const T& x) { T x2 = x; do_something_to_T(x2); return x2; }
T&& y = f(T());

This leaves y as an undefined dangling reference.

However, I don't understand why the above code even compiles? Is there ever a legitimate reason to assign a rvalue reference to another rvalue reference? Aren't rvalues suppose to be, roughly speaking, "temporaries", i.e. going to be made invalid at the end of the expression? Being able to assign them seems silly to me.

like image 920
Clinton Avatar asked Feb 29 '12 05:02

Clinton


1 Answers

Aren't rvalues suppose to be, roughly speaking, "temporaries", i.e. going to be made invalid at the end of the expression?

No, they're not.

Given your function f, this is just as legal:

T t{};
T&& y = f(std::move(t));

This is perfectly valid C++11 code. And it's also well-defined what happens.

The only reason your code is undefined is because you pass a temporary. But r-value references don't have to be temporaries.

To elaborate a bit more, an r-value reference conceptually is a reference to a value from which certain operations are considered OK to do, which would not otherwise be OK to do.

R-value references are very carefully specified in C++11. An l-value reference can be bound to any non-temporary without the need for a cast or anything:

T t{};
T &y = t;

An r-value reference can only be implicitly bound to a temporary or other "xvalue" (an object that is most certainly going to go away in the near future):

T &&x = T{};
T &&no = t; //Fail.

In order to bind an r-value reference to a non-xvalue, you need to do an explicit cast. The way C++11 spells this cast is telling: std::move:

T &&yes = std::move(t);

The "certain operations" I was speaking of was "moving". It is OK to move from an object under exactly two conditions:

  1. It is going to go away anyway. IE: a temporary.
  2. The user has explicitly said to move from it.

And these are the only two cases where an r-value reference can bind to something.

There are exactly two reasons r-value references exist: to support move semantics and to support perfect forwarding (which required a new reference type that they could hook funky casting mechanics onto, as well as potentially move semantics). Therefore, if you're not doing one of these two operations, the reasons to use a && are dubious.

like image 183
Nicol Bolas Avatar answered Oct 12 '22 01:10

Nicol Bolas