As I read some articles, rvalue references and move semantics are usually described together. However as I understand, rvalue references are just references to rvalues and have nothing to do on their own with move semantics. And move semantics could be implemented probably without even using rvalue references. So the question is, why move constructor/operator= use rvalue references? Was it just to make it easier to write the code?
Consider the problem. There are two basic move operations we want to support: move "construction" and move "assignment". I use quotations there because we don't necessarily have to implement them with constructors or move assignment operators; we could use something else.
Move "construction" means creating a new object by transferring the contents from an existing object, such that deleting the old object doesn't deallocate resources now used in the new one. Move "assignment" means taking a pre-existing object and transferring the contents from an existing object, such that deleting the old object doesn't deallocate resources now used in the new one.
OK, so these are the operations we want to do. Well, how to do it?
Take move "construction". While we don't have to implement this with a constructor call, we really want to. We don't want to force people to do two-stage move construction, even if it's behind some magical function call. So we want to be able to implement movement as a constructor. OK, fine.
Here's problem 1: constructors have no names. Therefore, you can only differentiate them based on argument types and overloading resolution. And we know that the move constructor for an object of type T must take an object of type T as a parameter. And since it only needs one argument, it therefore looks exactly like a copy constructor.
OK, so now we need some way to satisfy overloading. We could introduce some standard library type, a std::move_ref. It would be like std::reference_wrapper, but it would be a distinct type. Therefore, you could say that a move constructor is a constructor that takes a std::move_ref<T>. Alright, fine: problem solved.
Only not; we now have new problems. Consider this code:
std::string MakeAString() { return std::string("foo"); }
std::string data = MakeAString();
Ignoring elision, C++11's expression value category rules state that a type which is returned from a function by value is an prvalue. And therefore, it will automatically be used by move constructors/assignment operators wherever possible. No need for std::move or the like.
To do it your way would require this:
std::string MakeAString() { return std::move(std::string("foo")); }
std::string data = std::move(MakeAString());
Both of those std::move calls would be needed to avoid copying. You have to move out of the temporary and into the return value, and then move out of the return value and into data (again, ignoring elision).
If you think that this is merely a minor annoyance, consider what else rvalue references buy us: perfect forwarding. Without the special reference-collapsing rules, you could not write a proper forwarding function that forwards copy and move semantics perfectly. std::move_ref would be a real C++ type; you couldn't just slap arbitrary rules like reference collapsing onto it like you can with rvalue references.
At the end of the day, you need some kind of language construct in place, not merely a library type. By making it a new kind of reference, you get to be able to define new rules for what can bind to that reference (and what cannot). And you get to define special reference-collapsing rules that make perfect forwarding possible.
The connection is that it is safe to move from an rvalue (because (in the absence of casts) rvalues refer to objects that are at the end of their lifespans), so a constructor that takes an rvalue reference can be safely implemented by pilfering/moving from the referenced object.
From a C++-language point of view, this is the end of the connection, but the standard library further expands on this connection by consistently making construction from lvalues copy and construction from rvalues move, and by providing helper functions (such as std::move) which make it straightforward to chose whether to move or copy a particular object (by changing around the value category of the object in the expression that causes the copy/move).
Move semantics can be implemented without rvalue-references, but it would be a lot less neat. A number of problems would need to be solved:
How to capture an rvalue by non-const reference?
How to distinguish between a constructor that copies and a constructor that moves?
How to ensure that moves are used wherever they would be a safe optimization?
How to write generic code that works with both movable and copyable objects?
Related
After learning some Rust and its lifetime specifiers, borrowing semantics, etc, I came across a Rust sample which doesn't allow something like that which is allowed in C++. Why?
struct S {
std::string& str;
S(std::string&& value) : str(value) {}
};
It is not allowed and would cause an error if you actually did try that.
However, the name of a variable is always an lvalue, not a rvalue. What type the variable is doesn't matter at all. You need to call std::move on it to turn it into a rvalue. That's what std::move does.
When using a rvalue reference variable it behaves exactly like a lvalue reference variable. Both refer directly to the bound object as a lvalue. They only differ in how they can or cannot be initialized and how they affect overload resolution and template argument deduction.
The point is to make it explicit when you potentially move from an object. Even if value is a rvalue reference, you may still use it multiple times in the function. But, usually, when you move from the referenced object, you can't use it afterwards anymore or at least it will lose its state. Therefore it must be clear where a potential move can happen while still allowing non-move usage. So the rule makes sense to enforce you to write std::move explicitly everywhere where a move might happen.
If the goal is prevent a user from constructing S from a temporary, which is reasonable, then you should delete the constructor completely:
S(std::string&& value) = delete;
(There are technically some issues with this. A better, but complex approach is to follow what std::reference_wrapper does.)
However, the user already has to think about lifetime of the argument they pass to S. So you only really catch a small subset of potential mistakes with this. There is no way to protect the user from not keeping the passed object alive long enough. If you need to ensure this through S, then S must take (shared) ownership of the object, e.g. by using std::unique_ptr or std::shared_ptr instead of a reference. There is no borrow checker in C++ like there is in Rust to verify that the borrowed/referenced object is kept alive long enough.
C++ lifetime and ownership management is mostly based on convention together with some utilities like the smart pointers. There is no intrinsic core language enforcement aside from automatic storage duration, although core language features like rvalue references are designed to support the conventions, e.g. as I described above.
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?
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.
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.
Why should I use copy constructor based on move-semantics? I mean I can use non-const reference and do the same: take data from the object without copying. Or no?
Well, yes, you can - auto_ptr tried it, for example - but it doesn't work very well: for example, you cannot put objects with such a destructive copy into standard containers, because standard containers might need a copy that doesn't destroy the original. It would be surprising if e.g. insert destroyed the source object, but on the other hand, there are cases (say when the source is a temporary) when moving makes perfect sense. So C++, as usual, allows both cases (insert is overloaded for copy and move) and allows (some would say "forces") you to choose between them.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is move semantics?
I recently attended a C++11 seminar and the following tidbit of advice was given.
when you have && and you are unsure, you will almost always use std::move
Could any one explain to me why you should use std::move as opposed to some alternatives and some cases when you should not use std::move?
First, there's probably a misconception in the question I'll address:
Whenever you see T&& t in code (And T is an actual type, not a template type), keep in mind the value category of t is an lvalue(reference), not an rvalue(temporary) anymore. It's very confusing. The T&& merely means that t is constructed from an object that was an rvalue 1, but t itself is an lvalue, not an rvalue. If it has a name (in this case, t) then it's an lvalue and won't automatically move, but if it has no name (the result of 3+4) then it is an rvalue and will automatically move into it's result if it can. The type (in this case T&&) has almost nothing to do with the value category of the variable (in this case, an lvalue).
That being said, if you have T&& t written in your code, that means you have a reference to a variable that was a temporary, and it is ok to destroy if you want to. If you need to access the variable multiple times, you do not want to std::move from it, or else it would lose it's value. But the last time you acccess t it is safe to std::move it's value to another T if you wish. (And 95% of the time, that's what you want to do). All of this also applies to auto&& variables.
1. if T is a template type, T&& is a forwarding reference instead, in which case you use std::forward<T>(t) instead of std::move(t) the last time. See this question.
I found this article to be pretty enlightening on the subject of rvalue references in general. He mentions std::move towards the end. This is probably the most relevant quote:
We need to use std::move, from <utility> -- std::move is a way of
saying, "ok, honest to God I know I have an lvalue, but I want it to
be an rvalue." std::move does not, in and of itself, move anything; it
just turns an lvalue into an rvalue, so that you can invoke the move
constructor.
Say you have a move constructor that looks like this:
MyClass::MyClass(MyClass&& other): myMember(other.myMember)
{
// Whatever else.
}
When you use the statement other.myMember, the value that's returned is an lvalue. Thus the code uses the copy constructor to initialize this->myMember. But since this is a move constructor, we know that other is a temporary object, and therefore so are its members. So we really want to use the more-efficient move constructor to initialize this->myMember. Using std::move makes sure that the compiler treats other.myMember like an rvalue reference and calls the move constructor, as you'd want it to:
MyClass::MyClass(MyClass&& other): myMember(std::move(other.myMember))
{
// Whatever else.
}
Just don't use std::move on objects you need to keep around - move constructors are pretty much guaranteed to muck up any objects passed into them. That's why they're only used with temporaries.
Hope that helps!
When you have an object of type T&&, a rvalue, it means that this object is safe to be moved, as no one else will depend on its internal state later.
As moving should never be more expensive than copying, you will almost always want to move it. And to move it, you have to use the std::move function.
When should you avoid std::move, even if it would be safe? I wouldn't use it in trivial examples, e.g.,:
int x = 0;
int y = std::move(x);
Beside that, I see no downsides. If it does not complicate the code, moving should be done whenever possible IMHO.
Another example, where you don't want to move are return values. The language guarantees that return values are (at least) moved, so you should not write
return std::move(x); // not recommended
(If you are lucky, return value optimization hits, which is even better than a move operation.)
You can use move when you need to "transfer" the content of an object somewhere else, without doing a copy. It's also possible for an object to take the content of a temporary object without doing a copy, with std::move.
Read more on Rvalue references and move constructors from wikipedia.
I'm trying to figure out when to use move semantics and when to use a copy constructor and assignment operator as a rule of thumb. The type of pointer you use (if any) in your class seems to be affected by this answer, so I have included this.
No pointers - Based on this answer, if you have a POD class with primitive types like int and string, you don't need to write custom move or copy constructors and operators.
unique-ptr - Based on this answer, when using move semantics, then unique_ptr is a better fit over shared_ptr as there can only be one unique_ptr to the resource.
shared_ptr - Equally, if using copy semantics, shared_ptr seems the way to go. There can be multiple copies of the object, so having a shared pointer to the resource makes sense to me. However, unique_ptr is generally preferred to shared_ptr so avoid this option if you can.
But:
When should I use move semantics?
When should I use copy semantics?
Should I ever use both?
Should I ever use none and rely on the default copy constructor and assignment operator?
As the name indicates, use unique_ptr when there must exist exactly one owner to a resource. The copy constructor of unique_ptr is disabled, which means it is impossible for two instances of it to exist. However, it is movable... Which is fine, since that allows transfer of ownership.
Also as the name indicates, shared_ptr represents shared ownership of a resource. However, there is also another difference between the two smart pointers: The Deleter of a unique_ptr is part of its type signature, but it is not part of the type signature of shared_ptr. That is because shared_ptr uses "type erasure" to "erase the type" of the deleter. Also note that shared_ptr can also be moved to transfer ownership (like unique_ptr.)
When should I use move semantics?
Although shared_ptr can be copied, you may want to move them anyways when you are making a transfer of ownership (as opposed to creating a new reference). You're obligated to use move semantics for unique_ptr, since ownership must be unique.
When should I use copy semantics?
In the case of smart pointers, you should use copying to increase the reference count of shared_ptrs. (If you're unfamiliar with the concept of a reference count, research reference counted garbage collection.)
Should I ever use both?
Yes. As mentioned above, shared_ptr can be both copied and moved. Copying denotes incrementing the reference count, while moving only indicates a transfer of ownership (the reference count stays the same.)
Should I ever use none and rely on the default copy constructor and assignment operator?
When you want to make a member-by-member copy of an object.
When should I use move semantics?
I presume by this you mean, "When should I give my class a move constructor?" The answer is whenever moving objects of this type is useful and the default move constructor doesn't do the job correctly. Moving is useful when there is some benefit to transferring resources from one object to another. For example, moving is useful to std::string because it allows objects to be copied from temporaries without having to reallocate and copy their internal resources and instead by simply moving the resource from one to the other. Many types would benefit from this. On the other hand, moving is useful to std::unique_ptr because it is the only way to pass a std::unique_ptr around by value without violating its "unique ownership".
When should I use copy semantics?
Again, I presume by this you mean, "When should I give my class a copy constructor?" Whenever you need to be able to make copies of an object, where internal resources are copied between them, and the default copy constructor doesn't do the job correctly. Copying is useful for almost any type, except those like std::unique_ptr that must enforce unique ownership over an internal resource.
Should I ever use both?
Your classes should provide both copy and move semantics most of the time. The most common classes should be both copyable and moveable. Being copyable provides the standard semantics of passing around an object by value. Being moveable allows the optimisation that can be gained when passing around a temporary object by value. Whether this means having to provide a copy or move constructor depends on whether the default constructors do the appropriate things.
Should I ever use none and rely on the default copy constructor and assignment operator?
The default copy and move constructors just do a copy or move of each member of the class respectively. If this behaviour is appropriate for copying and moving your class, that's great. Most of the time, this should be good enough. For example, if I have a class that contains a std::string, the default copy constructor will copy the string over, and the default move constructor will move the string's resources to the new object - both do the appropriate job. If your class contains a std::unique_ptr, copying will simply not work, and your class will only be moveable. That may be what you want, or you may want to implement a copy constructor that performs a deep copy of the resource. The most important case in which you should implement copy/move constructors is when your class performs resource management itself (using new and delete, for example). If that's the case, the default constructors will almost never be doing a good job of managing those resources.
Everything here applies similarly to the assignment operator.