Enlico
Enlico

Reputation: 28490

What is the "source object is lvalue" scenario, mentioned in EMC++, where move semantics offer no efficiency gain

Item 29 from Effective Modern C++, Scott Meyers lists three scenarios where move semantics don't improve code's performance,

[…] move semantics do you no good:

  • No move operations: The object to be moved from fails to offer move operations […]
  • Move not faster: […] move operations that are no faster than its copy operations.
  • Move not usable: The context […] requires a move operation that emits no exceptions, but that operation isn't declared noexcept.

which are all clearly explained in the preceding pages, and then adds another one

[…] another scenario where move semantics offers no efficiency gain:

  • Source object is lvalue: With very few exceptions (see e.g. Item 25) only rvalues may be used as the source of a move operation.

(Item 25 is titled Use std::move on rvalue references and std::forward on universal references, but I don't see how it is related to the bullet point that cross-references it.)

After this, the text essentially goes back to summarizing the item, with no further reference to that fourth bullet point.

What does that bullet point refer to?

As far as I understand move semantics, even if I want to move from an lvalue, say x, I still need to cast it to an rvalue via std::move(x) (or an equivalent static_cast), so I'm technically still moving from an rvalue (specifically an xvalue in this case), not an lvalue.

So I'd be tempted to say that an lvalue cannot be the source object of a move operation.

What am I missing about this topic?

Upvotes: 3

Views: 141

Answers (3)

Enlico
Enlico

Reputation: 28490

HolyBlackCat had seen right, the answer is indeed in Item 25, towards the end:

compilers may elide the copying (or moving) of a local object […]

Widget makeWidget() {
  Widget w;
  //…
  return w;
}

[…] every decent C++ compiler will emply the RVO to avoid copying w.

[…] if the conditions for the RVO are met, but compilers choose not to perform copy elision, the object being returned must be treated as an rvalue. In effect, the Standard requires that when the RVO is permitted, either copy elision takes places or std::move is implicitly applied […]

Upvotes: 0

Dietmar Kühl
Dietmar Kühl

Reputation: 154015

The term lvalue refers to somehow “named” values, i.e., entities having multiple references. Move semantics don’t really apply to them as you shouldn’t “steal” the representation of something which may be referred to elsewhere. That is, if the source object is an lvalue you simply never move! So, move construction doesn’t provide a benefit here. In fact, lvalues don’t bind willingly to rvalue references - you’d beed to force that binding, e.g., by using std::move().

Essentially, your point is entirely correct: an lvalue cannot be the source of a move operation - and hence move operations don’t provide a benefit where lvalues are involved.

Upvotes: 1

gnasher729
gnasher729

Reputation: 52612

As an example: You have a class T with a move constructor. You have a function returning an object of type T, and you try to make it faster by returning in r-value. Now if you start with

T x;
x.a = ...;
x.b = ...;
x.c = ...;
return x;

then an object x will be constructed, a new unnamed object will be created by the return statement, then x is destructed, then the return value is moved. And eventually the caller will call the destructor for the moved result. So you have two constructors, two destructors, no savings.

If you start with

T x(a, b, c);
return x;

then you have the same problem, two constructors and destructors, no savings. To actually save anything, you need to write

return T(a, b, c);

or return the return value of another function returning an object.

Upvotes: -1

Related Questions