This is a very very common mistake to make as people first learn about rvalue references. The basic problem is a confusion between type and value category.
int
is a type. int&
is a different type. int&&
is yet another type. These are all different types.
lvalues and rvalues are things called value categories. Please check out the fantastic chart here: What are rvalues, lvalues, xvalues, glvalues, and prvalues?. You can see that in addition to lvalues and rvalues, we also have prvalues and glvalues and xvalues, and they form a various venn diagram sort of relation.
C++ has rules that say that variables of various types can bind to expressions. An expressions reference type however, is discarded (people often say that expressions do not have reference type). Instead, the expression have a value category, which determines which variables can bind to it.
Put another way: rvalue references and lvalue references are only directly relevant on the left hand of the assignment, the variable being created/bound. On the right side, we are talking about expressions and not variables, and rvalue/lvalue reference-ness is only relevant in the context of determining value category.
A very simple example to start with is simple looking at things of purely type int
. A variable of type int
as an expression, is an lvalue. However, an expression consisting of evaluating a function that returns an int
, is an rvalue. This makes intuitive sense to most people; the key thing though is to separate out the type of an expression (even before references are discarded) and its value category.
What this is leading to, is that even though variables of type int&&
can only bind to rvalues, does not mean that all expressions with type int&&
, are rvalues. In fact, as the rules at http://en.cppreference.com/w/cpp/language/value_category say, any expression consisting of naming a variable, is always an lvalue, no matter the type.
That's why you need std::move
in order to pass along rvalue references into subsequent functions that take by rvalue reference. It's because rvalue references do not bind to other rvalue references. They bind to rvalues. If you want to get the move constructor, you need to give it an rvalue to bind to, and a named rvalue reference is not an rvalue.
std::move
is a function that returns an rvalue reference. And what's the value category of such an expression? An rvalue? Nope. It's an xvalue. Which is basically an rvalue, with some additional properties.
foo
, the object designated bya
is local to the function, so it's guaranteed that the object expires and is safe to move from. Inbar, nothing is known about the object designated by
a`, so you don't want to modify it silently. Remember that rvalue references are just another kind of reference; the core language has no opinion on what you use it for, nor does it have any expectations on lifetime or aliases. – Ungroundeda
infoo
an implementation behavior? I failed to find any relevant description about that behavior in the standard. – Uranalysisa
is guaranteed to expire oncefoo
is out of scope, the move constructor is called. – Ceriaa
(infoo
) is an lvalue, then copy constructor should be called; that's all what I found in the standard. I can't find any quotes about that ifa
is to be expired then move constructor could be used instead. That's why I'm guessing that this is an implementation behavior, not guaranteed by the standard. – Uranalysis