Why does C++11 have implicit moves for value parameters, but not for rvalue parameters?

The standardization committee expended great effort in creating wording so that moves would only ever happen in exactly two circumstances:

  1. When it is clearly safe to do so.
  2. When the user explicitly asks (via std::move or a similar cast).

A value parameter will unquestionably be destroyed at the end of the function. Therefore, returning it by move is clearly safe; it can't be touched by other code after the return (not unless you're deliberately trying to break things, in which case you probably triggered undefined behavior). Therefore, it can be moved from in the return.

A && variable could be referring to a temporary. But it could be referring to an lvalue (a named variable). It is therefore not clearly safe to move from it; the original variable could be lurking around. And since you didn't explicitly ask to move from it (ie: you didn't call std::move in this function), no movement can take place.

The only time a && variable will be implicitly moved from (ie: without std::move) is when you return it. std::move<T> returns a T&&. It is legal for that return value to invoke the move constructor, because it is a return value.

Now it is very difficult to call A func(A &&a) with an lvalue without calling std::move (or an equivalent cast). So technically, it should be fine for parameters of && type to be implicitly moved from. But the standards committee wanted moves to be explicit for && types, just to make sure that movement didn't implicitly happen within the scope of this function. That is, it can't use outside-of-function knowledge about where the && comes from.

In general, you should only take parameters by && in two cases: either you're writing a move constructor (or move assignment operator, but even that can be done by value), or you're writing a forwarding function. There may be a few other cases, but you shouldn't take && to a type unless you have something special in mind. If A is a moveable type, then just take it by value.


In your first case, the compiler knows that a is going away and nothing will be able to cling on to it: clearly, this object can be moved from and if it is not it will be destroyed. In the second case, the rvalue reference indicates that it is permissible to move from the object and the caller doesn't expect the object to stay around. However, it is the function's choice whether it takes advantage of this permission or not and there may be reasons why the function sometimes wants to move from the argument and sometimes it doesn't want to. If the compiler were given the liberty to move off this object, there would be no way to prevent the compiler from doing so. However, using std::move(a) there is already a way to indicate that it is desired to move from the object.

The general rule in the standard is that the compiler only ever moves objects implicitly which are known to go away. When an rvalue reference comes in, the compiler doesn't really know that the object is about to away: if it was explicitly std::move()ed it actually stays around.


This was fixed for C++20 by P0527 and P1825. The only way to have a function parameter bind to an rvalue reference is for the source to either be a temporary or for the caller to explicitly cast a non-temporary to an rvalue (for instance, with std::move). Therefore, this "mandatory optimization" was deemed safe.