In C++11, "move semantics" was introduced, implemented via the two special members: move constructor and move assignment. Both of these operations leave the moved-from object constructed.
Wouldn't it have been better to leave the source in a destructed state? Isn't the only thing you can do with a moved-from object is destruct it anyway?
In the "universe of move operations" there are four possibilities:
target source
is is left
----------------------------------------------------------
constructed <-- constructed // C++11 -- move construction
constructed <-- destructed
assigned <-- constructed // C++11 -- move assignment
assigned <-- destructed
Each of these operations is useful! std::vector<T>::insert alone could make use of the first three. Though note:
The source of the 2nd and 4th should not have automatic, static or thread storage duration. The storage duration of the source must be dynamic, else the compiler will call the destructor on the already destructed object. And no, the compiler can not (in general) track if an object has been moved-from:
X x1, x2;
if (sometimes)
{
x1 = std::move(x2);
}
// Is x2 moved-from here?
The 2nd and 4th can be emulated by the 1st and 3rd respectively, by simply manually calling the destructor on the source after the operation.
The 1st and 3rd are crucial. Algorithms such as std::swap and std::sort regularly need both of these operations. These algorithms do not need to destruct any of their input objects — only change their values.
Armed with this knowledge, in the 2001-2002 time frame I focused my efforts on the two operations that left their source constructed because these two operations would have the largest (positive) impact on what was then C++98. I knew at the time that if I did not curtail the ambitions of this project, that it would never succeed. Even curtailed, it was borderline too-ambitious to succeed.
This curtailment is acknowledged in the original move semantics proposal under the section titled "Destructive move semantics".
In the end, we simply gave up on this as too much pain for not
enough gain. However the current proposal does not prohibit
destructive move semantics in the future. It could be done in
addition to the non-destructive move semantics outlined in this
proposal should someone wish to carry that torch.
For more details on what one can do with a moved-from object, see https://stackoverflow.com/a/7028318/576911
Related
For example if we had a 10 element vector, and we decided to erase the 5th element, all elements after the 5th would have to be moved back one element. Why is the assignment operator used here? Why not just copy the bits since we know the old copy is about to be overwritten / invalidated?
Obviously this changed we the move constructor. Let's just assume a move constructor / assignment isn't used here.
The fundamental reason is so that C++ objects can preserve class invariants involving the address of the object (or its sub-objects)
Therefore if the value of an object is to be established at a different address the class might need to execute code to preserve the invariant. That code goes in the move/copy constructor/assignment and the swap function if applicable.
A (possibly over-elaborate) example of such an invariant is, "exactly one sub-object is registered with some global collection of pointers, but which one varies between objects". If all you do is copy the bits, then there's no opportunity to update the global collection with the "right" sub-object according to the object state.
There is a serious limitation in C++03, that there are classes for which it is appropriate to just copy all the bits in the case you describe, but where it is not appropriate to just copy all the bits for a copy assignment in general, and a copy is unnecessarily expensive in your case. That limitation is what C++11 move semantics address, giving the class the chance to behave differently.
For standard copy constructors and assignment operators, I always think about implementing them or deleteing the defaults out of existence, if my class implements a destructor.
For the new move constructor and move operator, what is the right way to think about whether or not an implementation is necessary?
As a first pass of transitioning a system from pre-C++0x, could I just delete the default move constructor and move operator or should I leave them alone?
You don't have to worry about it, in the sense that when you user-declare a destructor (or anything else listed in 12.8/9), that blocks the default move constructor from being generated. So there's not the same risk as there is with copies, that the default is wrong.
So, as the first pass leave them alone. There may be places in your existing code where C++11 move semantics allow a move, whereas C++03 dictates a copy. Your class will continue to be copied, and if that caused no performance problems in C++03 then I can't immediately think of any reason why it would in C++11. If it did cause performance problems in C++03, then you have an opportunity to fix a bug in your code that you never got around to before, but that's an opportunity, not an obligation ;-)
If you later implement move construction and assignment, they will be moved, and in particular you'll want to do this if you think that C++11 clients of your class are less likely to use "swaptimization" to avoid copies, more likely to pass your type by value, etc, than C++03 clients were.
When writing new classes in C++11, you need to consider and implement move under the same criteria that you considered and implemented swap in C++03. A class that can be copied implements the C++11 concept of "movable", (much as a class that can be copied in C++03 can be swapped via the default implementation in std), because "movable" doesn't say what state the source is left in - in particular it's permitted to be unchanged. So a copy is valid as a move, it's just not necessarily an efficient one, and for many classes you'll find that unlike a "good" move or swap, a copy can throw.
You might find that you have to implement move for your classes in cases where you have a destructor (hence no default move constructor), and you also have a data member which is movable but not copyable (hence no default copy constructor either). That's when move becomes important semantically as well as for performance.
With C++11, you very rarely need to provide a destructor or copy semantics, due to the way the library is written. Compiler provided members pretty much always do fine (provided they are implemented correctly: MSVC forces you to implement a lot of move semantics by hand, which is very bothersome).
In case you have to implement a custom destructor, use the following approach:
Implement a move constructor, and an assignment operator taking by value (using copy&swap: note that you cannot use std::swap since it uses the assignment. You have to provide a private swap yourself). Pay attention to exception guarantees (look up std::move_if_noexcept).
If necessary, implement a copy constructor. Otherwise, delete it. Beware that non default copy semantics rarely make sense.
Also, a virtual destructor counts as a custom destructor: provide or delete copy + move semantics when declaring a virtual destructor.
Since they are used as an optimization you should implement them if the optimization is applicable to your class. If you can "steal" the internal resource your class is holding from a temporary object that is about to be destroyed. std::vector is a perfect example, where move constructor only assigns pointers to internal buffer leaving the temporary object empty (effectively stealing the elements).
Say I have two vectors and I move one unto the other, v1 = std::move(v2); will v2 still be in a usable state after this?
From n3290, 17.6.5.15 Moved-from state of library types [lib.types.movedfrom]
Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
Since the state is valid, this means you can safely operate on v2 (e.g. by assigning to it, which would put it back to a known state). Since it is unspecified however, it means you cannot for instance rely on any particular value for v2.empty() as long as it is in this state (but calling it won't crash the program).
Note that this axiom of move semantics ("Moved from objects are left in a valid but unspecified state") is something that all code should strive towards (most of the time), not just the Standard Library components. Much like the semantics of copy constructors should be making a copy, but are not enforced to.
No, it is left in an unspecified state.
Excerpt from open-std-org article -
.. move() gives its target the value of its argument, but is not obliged to preserve the value of its source. So, for a vector, move() could reasonably be expected to leave its argument as a zero-capacity vector to avoid having to copy all the elements. In other words, move is a potentially destructive read.
If you want to use v2 after the move, you will want to do something like:
v1 = std::move(v2);
v2.clear();
At this point, v1 will have the original contents of v2 and v2 will be in a well-defined empty state. This works on all of the STL containers (and strings, for that matter), and if you are implementing your own classes that support move-semantics, you'll probably want to do something similar.
If your particular implementation of the STL actually DOES leave the object in an empty state, then the second clear() will be essentially a no-op. In fact, if this is the case, it would be a legal optimization for a compiler to eliminate the clear() after the move.
I have already asked a similar question a while ago, but I'm still unclear on some details.
Under what circumstances is the postblit constructor called?
What are the semantics of moving an object? Will it be postblitted and/or destructed?
What happens if I return a local variable by value? Will it implicitly be moved?
How do I cast an expression to an rvalue? For example, how would a generic swap look like?
A postblit constructor is called whenever the struct is copied - e.g. when passing a struct to a function.
A move is a bitwise copy. The postblit constructor is never called. The destructor is never called. The bits are simply copied. The original was "moved" and so nothing needs to be created or destroyed.
It will be moved. This is the prime example of a move.
There are a number of different situations that a swap function would have to worry about if you want to make it as efficient as possible. I would advise simply using the swap function in std.algorithm. The classic swap would result in copying and would thus call the postblit constructor and the destructor. Moves are generally done by the compiler, not the programmer. However, looking at the official implementation of swap, it looks like it plays some tricks to get move semantics out of the deal where it can. Regardless, moves are generally done by the compiler. They're an optimization that it will do where it knows that it can (RVO being the classic case where it can).
According to TDPL (p. 251), there are only 2 cases where D guarantees that a move will take place:
All anonymous rvalues are moved, not copied. A call to this(this)
is never inserted when the source is an anonymous rvalue (i.e., a
temporary as featured in the function hun above).
All named temporaries that are stack-allocated inside a function and
then returned elide a call to this(this).
There is no guarantee that other potential elisions are observed.
So, the compiler may use moves elsewhere, but there's no guarantee that it will.
As far as I understand:
1) When a struct is copied, as opposed to moved or constructed.
2) The point of move semantics is that neither of the two needs to happen. The new location of the struct is initialized with a bit-wise copy of the struct, and the old location goes out of scope and becomes inaccessible. Thus, the struct has "moved" from A to B.
3) That is the typical move situation:
S init(bool someFlag)
{
S s;
s.foo = someFlag? bar : baz;
return s; // `s` can now be safely moved from here...
}
// call-site:
S s = init(flag);
//^ ... to here.
In an answer to a question about std::stack::pop() I claimed that the reason pop does not return the value is for exception safety reason (what happens if the copy constructor throws).
#Konrad commented that now with move semantics this is no longer relevant. Is this true?
AFAIK, move constructors can throw, but perhaps with noexcept it can still be achieved.
For bonus points what thread safety guarantees can this operation supply?
Of course, not every type is move-enabled and C++0x even allows throwing move constructors. As long as constructing the object from an rvalue may throw it cannot be exception-safe. However, move semantics allows you to have many types that are nothrow-constructible given an rvalue source.
Conditional support for this could be done with SFINAE. But even without such a conditional member function nothing stops you from writing:
auto stack = ...;
auto elem = std::move_if_noexcept(stack.back());
stack.pop_back();
which is makes the strong exception guarantee even in case your move constructor doesn't give the strong guarantee.
As for the bonus question, that would offer no thread-safety. Consider that, as an example, most implementations of std::vector have three data elements (pointer to beginning of memory, pointer one beyond end of used data, pointer one beyond end of allocated memory). Move semantics allow you to move the contents of the vector without the need to reallocate and copy values, but that has nothing to do with thread safety. You would have to use thread-safe constructs to make the structure thread safe (as moving does not imply by any means atomic)