The standardization committee expended great effort in creating wording so that moves would only ever happen in exactly two circumstances:
- When it is clearly safe to do so.
- 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.