2

As many other posts explain, declaring one variable as an rvalue reference does not guarantee that it will call the move assignment operator, only using std::move (that is, casting to that same rvalue reference) will do it. For example:

struct A
{
    A& operator=(A&& l_other) { std::cout << "move" << std::endl; }
    A& operator=(A&  l_other) { std::cout << "copy" << std::endl; }
};

A a;

// does not print anything, compiler does nothing here!
A&& b = std::move(a);

// prints "copy", not "move"
a = b;

// prints "move", but needs std::move()
a = std::move(b);

Seems that defining b as A&& does not move anything in that moment. After that, a = b does not move b automatically, even if right part is defined as A&&, as other posts explain. We need to explicitly call std::move(b) to move b.

My question is, what's the utility of defining a variable as A&&? I could perfectly define it as A& and move it exactly the same.

underscore_d
  • 6,309
  • 3
  • 38
  • 64
Victor
  • 460
  • 3
  • 11
  • you should not move lvalue reference (I refer to `l_other`) – apple apple Nov 14 '19 at 10:24
  • 4
    To address *why* `std::move` is needed, refer to [`std::move`](https://en.cppreference.com/w/cpp/utility/move) - specifically this quote from the notes : "Names of rvalue reference variables are lvalues and have to be converted to xvalues to be bound to the function overloads that accept rvalue reference parameters". Yes, it's complicated :) – Sander De Dycker Nov 14 '19 at 10:27
  • 3
    @Victor The reason why the assignment `a = b` triggers the copy assignment is because b is actually an `lvalue` with the type of `rvlaue reference to A`. The reason to declare an rvalue reference is to catch, well, rvalues, which you can't catch with `A&`. – Petok Lorand Nov 14 '19 at 10:30
  • 2
    You declare them when you want to distinguish from `A&`, i.e. as parameters. I can't think of any situations where you would want an `A&&` as a local variable, over `A` or `A &` – Caleth Nov 14 '19 at 10:43
  • @Caleth you can assign `A&&` to `A&` directly. – darune Nov 14 '19 at 10:46
  • 5
    R-value references (such as `A&&` in your case) are rarely declared in function bodies. They are mostly used as function parameters. Anyway, a simple rule of thumb is that anything that has a name (`b`) is an lvalue. – Daniel Langr Nov 14 '19 at 10:50
  • 1
    this might be interesting to you: https://stackoverflow.com/q/45843974/2805305 – bolov Nov 14 '19 at 12:34

2 Answers2

2

My question is, what's the utility of defining a variable as A&&? I could perfectly define it as A& and move it exactly the same.

If a named variables is passed into a function and modified like that without warning it's very displeasing, and can lead to unpleasant debugging sessions.

When you take by rvalue reference the type system enforces useful guarantees. Either the reference is bound to an rvalue, and so modifying it isn't going to violate anyone's assumptions (it's gone soon anyway). Or you were given explicit permission to move out of it. The calling context can't accidentally give you permission to modify an lvalue, it must do so with an explicit cast (std::move).

Explicit is better than implicit. And having rvalue references interact with the type system the way they do helps make sure moving does not leave objects in an unexpected state. A glance at the calling context reveals that the variable passed into the function may be left with an unknown state, because it's explicitly surrounded with a call to std::move.

That's the benefit of rvalue references.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

Think of it like this: There are two parties involved: a producer of an object (to be bound by the reference) and a consumer of the reference.

The kind of reference defines what is allowed to go into the reference (producer), not what is in the variable (consumer).

For the consumer, it makes no difference which kind of reference it is.

For the producer, an rvalue reference is a sign that only an expendable value can be bound (a temporary or an xvalue such as the result of str::move).

j6t
  • 9,150
  • 1
  • 15
  • 35