7

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.

3
  • 2
    An expression referring to an rvalue reference variable is an lvalue, since you can refer to the variable multiple times. Commented Oct 23, 2014 at 16:34
  • 2
    Because "rvalue reference" is a bad and confusing name. And because C++ is bad and confusing. Commented Oct 23, 2014 at 16:37
  • 2
    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. Commented Oct 23, 2014 at 16:46

2 Answers 2

11

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.

Sign up to request clarification or add additional context in comments.

Comments

9

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.

4 Comments

An important point is why this rule is as it is. When he invokes a = v, the compiler has no way of knowing that he won't need v again (and in fact, he does use it later), so it cannot risk changing the value unless you specifically authorize it to.
@JamesKanze: That's right. As a golden rule, rvalues should guarantee to not be aliased. This isn't enforced or enforceable at the language level (other languages like Rust do enforce this), but it should be how one thinks about rvalues.
Another way to remember this is that an rvalue reference is talking about what can be bound to the reference: an rvalue. It doesn't mean that using the reference is the same as using an rvalue.
@KerrekSB The fundamental idea is that an rvalue reference binds to a "throw-away" object; an object that won't be used again. If the object is a temporary (an rvalue), or is being returned from a function, it's automatically a throw-away object, since there's no way it can be used again. Otherwise, you have to tell the compiler that you're not using the object again. The way to do that is with std::move.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.