Reputation: 28490
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
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
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
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