7
votes

I am learning c++11 and i have a question regarding move semantics and rvalue references. My sample code is as following (C++ Shell URL is cpp.sh/8gt):

#include <iostream>
#include <vector>

void aaa(std::vector<int>&& a)
{
    std::cout << "size of a before move: " << a.size() << std::endl;
    std::vector<int> v;
    v = a; /*std::move(a)*/
    std::cout << "size of a after move: " << a.size() << std::endl;
}

int main ()
{
  std::vector<int> foo (3,0);

  aaa(std::move(foo));

  return 0;
}

The result of the is:

size of a before move: 3  
size of a after move: 3

It seems the move assign operator of std::vector is not invoked at line v = a in function aaa, otherwise a would have size 0 instead of 3.
However if i change v = a to v = std::move(a) the output became

size of a before move: 3
size of a after move: 0

and I thinke the move assign operator of std::vector has been invoked this time.

My quesiton is why the assign operator is not invoked the first time? According to c++ reference std::vector has a assign operator which takes a rvalue reference.

copy (1) vector& operator= (const vector& x);
move (2) vector& operator= (vector&& x);
initializer list (3) vector& operator= (initializer_list il);

Then at line v = a, since a is declared as rvalue reference, the move operator should be invoked. Why we still need to wrap a with a std::move?

Many thanks in advance!

[edit] I think both Loopunroller and Kerrek SB answered my question. Thanks! I don't think I can choose two answers so I will just pick the first one.

2
An expression referring to an rvalue reference variable is an lvalue, since you can refer to the variable multiple times.dyp
Because "rvalue reference" is a bad and confusing name. And because C++ is bad and confusing.Lightness Races in Orbit
This is, of course, a good thing (in a bad and confusing way). Otherwise, v = a would silently modify a, causing exactly the kind of auto_ptr-style weirdness that move semantics were intended to avoid.Mike Seymour

2 Answers

11
votes

This note from [expr]/6 might clarify what is going on (emphasis mine):

[ Note: An expression is an xvalue if it is:

  • the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to object type,
  • a cast to an rvalue reference to object type,
  • a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or
  • a .* pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.

In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not. — end note ]

It is not hard to see that the expression std::move(a) is an xvalue according to the list above (bullet one).
a is the name of an rvalue reference, and thus an lvalue as an expression.

9
votes

The expression a is an lvalue. The expression std::move(a) is an rvalue. Only rvalues bind to rvalue references, which make up the move constructor and move assignment operator.

It is worth repeating this to yourself until it makes sense: Evaluating any reference variable, and also dereferencing any dereferenceable pointer, produces an lvalue.