Victor
Victor

Reputation: 470

When should we declare rvalue refs if they actually need std::move?

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.

Upvotes: 2

Views: 433

Answers (2)

j6t
j6t

Reputation: 13377

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).

Upvotes: 0

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.

Upvotes: 2

Related Questions