This question already has answers here:
What can I do with a moved-from object?
(2 answers)
Closed 9 years ago.
If an object is actually moved to another location, what are the operations supported on the original object?
To elaborate it, I have a type T with available move constructor. With the following statements
T x{constructor-args};
T y{std::move(x)};
what all can be done with the object x (provided that the object actually moves from x to y using available move constructor of T)?
Specifically,
x can be destroyed for sure. Can I assume x is trivially destructible after move?
can x be assigned or move assigned to a new object ( I guess yes, as I have seen many swap uses this)?
can I construct a new object object in place of x? If by any chance this is allowed, then is it possible to have a uninitialized_move which works with (partially) overlapped source & destination range, like std::copy and unlike std::uninitialized_copy ?
T z{some-args};
x = z; //or x = std::move(z);
T u{some-args};
new(&x) T{std::move(u)}; // or new(&x) T{u}; etc
Whatever you define it to be. At the least, the resulting
object should be destructable—its destructor will be
called, and the state of the object should be such that that
causes no problems.
The standard library guarantees that its objects are some
coherent state. You can call any of the member functions: it is
unspecified what you get in return, but the results will be
coherent: if you call size on a vector which has been moved,
you can also call operator[] with an index less than the value
returned by size (but in the only reasonable implementation,
size will return 0,). This is probably more than what is
needed for effective use, however; all that is really necessary
is that calling the destructor will work.
To make it clearer, using std::vector as an example: if we
suppose the usual implementation, with three pointers, begin,
end and limit (or however they are called: limit is meant to be
one past the end of the allocated memory, so that capacity()
returns limit - begin). After moving, the standard would
require that all three pointers be null. In most
implementations, the limit pointer will not be accessed in the
destructor, so the looser requirements of being deletable would
be met if only the begin and end pointers were set to null.
With regards to library types, the standard says (17.6.5.15)
Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
This means they should, at a minimum, be destructible and assignable. As James mentions below, the object should still comply with its own interface. Your own types should follow the same idea.
You should not be using placement-new for this sort of thing. Pretend the move constructor doesn't exist and write code the same way as before, using operator=. The last line in your example should be x=std::move(u); or x=u;.
Related
Have found comparable questions but not exactly with such a case.
Take the following code for example:
#include <iostream>
#include <string>
#include <vector>
struct Inner
{
int a, b;
};
struct Outer
{
Inner inner;
};
std::vector<Inner> vec;
int main()
{
Outer * an_outer = new Outer;
vec.push_back(std::move(an_outer->inner));
delete an_outer;
}
Is this safe? Even if those were polymorphic classes or ones with custom destructors?
My concern regards the instance of "Outer" which has a member variable "inner" moved away. From what I learned, moved things should not be touched anymore. However does that include the delete call that is applied to outer and would technically call delete on inner as well (and thus "touch" it)?
Neither std::move, nor move semantics more generally, have any effect on the object model. They don't stop objects from existing, nor prevent you from using those objects in the future.
What they do is ask to borrow encapsulated resources from the thing you're "moving from". For example, a vector, which directly only stores a pointer some dynamically-allocated data: the concept of ownership of that data can be "stolen" by simply copying that pointer then telling the vector to null the pointer and never have anything to do with that data again. It's yielded. The data belongs to you now. You have the last pointer to it that exists in the universe.
All of this is achieved simply by a bunch of hacks. The first is std::move, which just casts your vector expression to vector&&, so when you pass the result of it to a construction or assignment operation, the version that takes vector&& (the move constructor, or move-assignment operator) is triggered instead of the one that takes const vector&, and that version performs the steps necessary to do what I described in the previous paragraph.
(For other types that we make, we traditionally keep following that pattern, because that's how we can have nice things and persuade people to use our libraries.)
But then you can still use the vector! You can "touch" it. What exactly you can do with it is discoverable from the documentation for vector, and this extends to any other moveable type: the constraints emplaced on your usage of a moved-from object depend entirely on its type, and on the decisions made by the person who designed that type.
None of this has any impact on the lifetime of the vector. It still exists, it still takes memory, and it will still be destructed when the time comes. (In this particular example you can actually .clear() it and start again adding data to a new buffer.)
So, even if ints had any sort of concept of this (they don't; they encapsulate no indirectly-stored data, and own no resources; they have no constructors, so they also have no constructors taking int&&), the delete "touch"ing them would be entirely safe. And, more generally, none of this depends on whether the thing you've moved from is a member or not.
More generally, if you had a type T, and an object of that type, and you moved from it, and one of the constraints for T was that you couldn't delete it after moving from it, that would be a bug in T. That would be a serious mistake by the author of T. Your objects all need to be destructible. The mistake could manifest as a compilation failure or, more likely, undefined behaviour, depending on what exactly the bug was.
tl;dr: Yes, this is safe, for several reasons.
std::move is a cast to an rvalue-reference, which primarily changes which constructor/assignment operator overload is chosen. In your example the move-constructor is the default generated move-constructor, which just copies the ints over so nothing happens.
Whether or not this generally safe depends on the way your classes implement move construction/assignment. Assume for example that your class instead held a pointer. You would have to set that to nullptr in the moved-from class to avoid destroying the pointed-to data, if the moved-from class is destroyed.
Because just defining move-semantics is a custom way almost always leads to problems, the rule of five says that if you customize any of:
the copy constructor
the copy assignment operator
the move constructor
the move assignment operator
the destructor
you should usually customize all to ensure that they behave consistently with the expectations a caller would usually have for your class.
Using std::list supporting move semantics as an example.
std::list<std::string> X;
... //X is used in various ways
X=std::list<std::string>({"foo","bar","dead","beef"});
The most straightforward way for compiler to do the assignment since C++11 is:
destroy X
construct std::list
move std::list to X
Now, compiler isn't allowed to do following instead:
destroy X
contruct std::list in-place
because while this obviously saves another memcpy it eliminates assignment. What is the convenient way of making second behaviour possible and usable? Is it planned in future versions of C++?
My guess is that C++ still does not offer that except with writing:
X.~X();
new(&X) std::list<std::string>({"foo","bar","dead","beef"});
Am I right?
You can actually do it by defining operator= to take an initializer list.
For std::list, just call
X = {"foo","bar","dead","beef"}.
In your case, what was happening is actually:
Construct a temporary
Call move assignment operator on X with the temporary
On most objects, such as std::list, this won't actually be expensive compared to simply constructing an object.
However, it still incurs additional allocations for the internal storage of the second std::list, which could be avoided: we could reuse the internal storage already allocated for X if possible. What is happenning is:
Construct: the temporary allocates some space for the elements
Move: the pointer is moved to X; the space used by X before is freed
Some objects overload the assignment operator to take an initializer list, and it is the case for std::vector and std::list. Such an operator may use the storage already allocated internally, which is the most effective solution here.
// Please insert the usual rambling about premature optimization here
Is it planned in future versions of C++?
No. And thank goodness for that.
Assignment is not the same as destroy-then-create. X is not destroyed in your assignment example. X is a live object; the contents of X may be destroyed, but X itself never is. And nor should it be.
If you want to destroy X, then you have that ability, using the explicit-destructor-and-placement-new. Though thanks to the possibility of const members, you'll also need to launder the pointer to the object if you want to be safe. But assignment should never be considered equivalent to that.
If efficiency is your concern, it's much better to use the assign member function. By using assign, the X has the opportunity to reuse the existing allocations. And this would almost certainly make it faster than your "destroy-plus-construct" version. The cost of moving a linked list into another object is trivial; the cost of having to destroy all of those allocations only to allocate them again is not.
This is especially important for std::list, as it has a lot of allocations.
Worst-case scenario, assign will be no less efficient than whatever else you could come up with from outside the class. And best-case, it will be far better.
When you have a statement involving a move assignment:
x = std::move(y);
The destructor is not called for x before doing the move. However, after the move, at some point the destructor will be called for y. The idea behind a move assignment operator is that it might be able to move the contents of y to x in a simple way (for example, copying a pointer to y's storage into x). It also has to ensure its previous contents are destroyed properly (it may opt to swap this with y, because you know y may not be used anymore, and that y's destructor will be called).
If the move assignment is inlined, the compiler might be able to deduce that all the operations necessary for moving storage from y to x are just equivalent to in-place construction.
Re your final question
” Am I right?
No.
Your ideas about what's permitted or not, are wrong. The compiler is permitted to substitute any optimization as long as it preserves the observable effects. This is called the "as if" rule. Possible optimizations include removing all of that code, if it does not affect anything observable. In particular, your "isn't allowed" for the second example is completely false, and the reasoning "it eliminates assignment" applies also to your first example, where you draw the opposite conclusion, i.e. there's a self-contradiction right there.
An example move constructor implementation from a C++ course I’m taking looks a bit like this:
/// Move constructor
Motorcycle::Motorcycle(Motorcycle&& ori)
: m_wheels(std::move(ori.m_wheels)),
m_speed(ori.m_speed),
m_direction(ori.m_direction)
{
ori.m_wheels = array<Wheel, 2>();
ori.m_speed = 0.0;
ori.m_direction = 0.0;
}
(m_wheels is a member of type std::array<Wheel, 2>, and Wheel only contains a double m_speed and a bool m_rotating. In the Motorcycle class, m_speed and m_direction are also doubles.)
I don’t quite understand why ori’s values need to be cleared.
If a Motorcycle had any pointer members we wanted to “steal”, then sure, we’d have to set ori.m_thingy = nullptr so as to not, for example, delete m_thingy twice. But does it matter when the fields contain the objects themselves?
I asked a friend about this, and they pointed me towards this page, which says:
Move constructors typically "steal" the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For example, moving from a std::string or from a std::vector may result in the argument being left empty. However, this behaviour should not be relied upon.
Who defines what indeterminate state means? I don’t see how setting the speed to 0.0 is any more indeterminate than leaving it be. And like the final sentence in the quote says — code shouldn’t rely on the state of the moved-from Motorcycle anyway, so why bother cleaning it up?
They don't need to be cleaned. The designer of the class decided it would be a good idea to leave the moved-from object zero initialized.
Note that a situation where is does make sense is for objects managing resources that get released in the destructor. For instance, a pointer to dynamically allocated memory. Leaving the pointer in the moved-from object unmodified would mean two objects managing the same resource. Both their destructors would attempt to release.
Who defines what indeterminate state means?
The author of the class.
If you look at the documentation for std::unique_ptr, you'll see that after the following lines:
std::unique_ptr<int> pi = std::make_unique<int>(5);
auto pi2 = std::move(pi);
pi is actually in a very defined state. It will have been reset() and pi.get() will be equal to nullptr.
You're very probably correct that the author of this class is putting unnecessary operations in the constructor.
Even if m_wheels were a heap-allocated type, such as std::vector, there would still be no reason to "clear" it, since it is already being passed to its own move-constructor:
: m_wheels(std::move(ori.m_wheels)), // invokes move-constructor of appropriate type
However, you have not shown enough of the class to permit us to know whether the explicit "clearing" operations are necessary in this particular case. As per Deduplicator's answer, the following functions are expected to behave correctly in the "moved-from" state:
// Destruction
~Motorcycle(); // This is the REALLY important one, of course
// Assignment
operator=(const Motorcycle&);
operator=(Motorcycle&&);
Therefore, you must look at the implementation of each of these functions in order to determine whether the move-constructor is correct.
If all three use the default implementation (which, from what you've shown, seems reasonable), then there's no reason to manually clear the moved-from objects. However, if any of these functions use the values of m_wheels, m_speed, or m_direction to determine how to behave, then the move-constructor may need to clear them in order to ensure the correct behavior.
Such a class design would be inelegant and error-prone, since typically we would not expect the values of primitives to matter in the "moved-from" state, unless the primitives are explicitly used as flags to indicate whether clean-up must occur in the destructor.
Ultimately, as an example for use in a C++ class, the move-constructor shown is not technically "wrong" in the sense of causing undesired behavior, but it seems like a poor example because it is likely to cause exactly the confusion that led you to post this question.
There are few things which must be safely doable with a moved-from object:
Destruct it.
Assign / Move to it.
Any other operations which explicitly say so.
So, you should move from them as fast as possible while giving those few guarantees, and more only if they are useful and you can fulfill them for free.
Which often means not clearing the source, and other times implies doing it.
As an addition, prefer explicitly defaulted over user-defined functions, and implicitly defined ones over both, for brevity and preserving triviality.
There is no standard behavior for this. Like with pointer you can still use them after you deleted them. Some people see you should not care and just not reuse the object but the compiler won't prohibit that.
Here is one blogpost (not by me) about this with some interesting discussion in the comment:
https://foonathan.github.io/blog/2016/07/23/move-safety.html
and the follow up:
https://foonathan.github.io/blog/2016/08/24/move-default-ctor.html
And here with also a recent Video chat about this topic with arguments discussing this:
https://www.youtube.com/watch?v=g5RnXRGar1w
Basically it's about treating a moved-from object like a deleted pointer or making it in a safe to be "moved-from-state"
Who defines what indeterminate state means?
The English language, I think. "Indeterminate" here has one of its usual English meanings, "Not determinate; not precisely fixed in extent; indefinite; uncertain. Not established. Not settled or decided". The state of a moved-from object is not constrained by the standard other than that it must be "valid" for that object type. It need not be the same every time an object is moved from.
If we were only talking about types provided by the implementation, then the proper language would be a "valid but otherwise unspecified state". But we reserve the word "unspecified" for talking about details of the C++ implementation, not details of what user code is allowed to do.
"Valid" is defined separately for each type. Taking integer types as an example, trap representations are not valid and anything that represents a value is valid.
That standard here is not saying that the move constructor must make the object be indeterminate, merely that it needn't put it into any determined state. So although you're correct that 0 is not "more indeterminate" than the old value, it is in any case moot since the move constructor needn't make the old object "as indeterminate as possible".
In this case, the author of the class has chosen to put the old object into one specific state. It's then entirely up to them whether they document what that state is, and if they do then it's entirely up to users of the code whether they rely on it.
I would usually recommend not relying on it, because under certain circumstances code that you write thinking of it semantically being a move, actually does a copy. For example, you put std::move on the right-hand side of an assignment not caring whether the object is const or not because it works either way, and then somebody else comes along and thinks "ah, it's been moved from, it must have been cleared to zeros". Nope. They've overlooked that Motorcycle is cleared when moved from, but const Motorcycle of course is not, no matter what the documentation might suggest to them :-)
If you're going to set a state at all, then it's really a coin toss which state. You could set it to a "clear" state, perhaps matching what the no-args constructor does (if there is one), on the basis that this represents the most neutral value there is. And I suppose on many architectures 0 is the (perhaps joint-)cheapest value to set something to. Alternatively you could set it to some eye-catcher value, in the hope that when someone writes a bug where they accidentally move from an object and then use its value, they'll think to themselves "What? The speed of light? In a residential street? Oh yeah, I remember, that's the value this class sets when moved-from, I probably did that".
The source object is an rvalue, potentially an xvalue. So the thing one needs to be concerned with is the imminent destruction of that object.
Resource handles or pointers are the most significant item that distinguishes a move from a copy: after the operation the ownership of that handle is assumed to be transferred.
So obviously, as you mention in the question, during a move we need to affect the source object so that it no-longer identifies itself as owner of transferred objects.
Thing::Thing(Thing&& rhs)
: unqptr_(move(rhs.unqptr_))
, rawptr_(rhs.rawptr_)
, ownsraw_(rhs.ownsraw_)
{
the.ownsraw_ = false;
}
Note that I don't clear rawptr_.
Here a design decision is made that, as long as the ownership flag is only true for one object, we don't care if there is a dangling pointer.
However, another engineer might decide that the pointer should be cleared so that instead of random ub, the following mistake results in a nullptr access:
void MyClass::f(Thing&& t) {
t_ = move(t);
// ...
cout << t;
}
In the case of something as innocuous as the variables shown in the question, they may not need to be cleared but that depends on the class design.
Consider:
class Motorcycle
{
float speed_ = 0.;
static size_t s_inMotion = 0;
public:
Motorcycle() = default;
Motorcycle(Motorcycle&& rhs);
Motorcycle& operator=(Motorcycle&& rhs);
void setSpeed(float speed)
{
if (speed && !speed_)
s_inMotion++;
else if (!speed && speed_)
s_inMotion--;
speed_ = speed;
}
~Motorcycle()
{
setSpeed(0);
}
};
This is a fairly artificial demonstration that ownership isn't necessarily a simple pointer or bool, but can be an issue of internal consistency.
The move operator could use setSpeed to populate itself, or it could just do
Motorcycle::Motorcycle(Motorcycle&& rhs)
: speed_(rhs.speed_)
{
rhs.speed_ = 0; // without this, s_inMotion gets confused
}
(Apologies for typos or autocorrects, typed on my phone)
I just stumbled into an issue with std::vector when adding new elements to it.
It seems when you try to add more elements to it, and it needs to allocate more space, it does so by copying last element all elements it currently holds. This seems to assume that any element in the vector is fully valid, and as such a copy will always succeed.
In our case, this is not necessarily true. Currently we might have some left over elements in the vector, because we chose not to remove them yet, which are valid objects, but their data doesn't guarantee valid behavior. The objects have guards, but I never considered adding guards to a copy constructor as I assumed we would never be copying an invalid object (which vector forces) :
CopiedClass::CopiedClass(const CopiedClass& other)
: member(other.member)
{
member->DoSomething();
}
It just so happened that "member" is nulled when we are done with the original object and leave it lying about in the vector, so when std::vector tries to copy it, it crashes.
Is it possible to prevent std::vector from copying that element? Or do we have to guard against possible invalid objects being copied? Ideally we'd like to keep assuming that only valid objects are created, but that seems to imply we immediately clean them from the vector, rather than waiting and doing it at some later stage.
It's actually a design flaw in your CopiedClass that needs to be fixed.
std::vector behaves as expected and documented.
From the little code you show
member->DoSomething();
this indicates you are going to take a shallow copy of a pointer to what ever.
in our case, this is not necessarily true. Currently we might have some left over elements in the vector, because we chose not to remove them yet, which are valid objects, but their data doesn't guarantee valid behavior.
It just so happened that "member" is nulled when we are done with the original object and leave it lying about in the vector, so when std::vector tries to copy it, it crashes.
As your copy constructor doesn't handle that situation correctly regarding following the Rule of Three your design of CopiedClass is wrong.
You should either care to create a deep copy of your member, or use a smart pointer (or a plain instance member), rather than a raw pointer.
The smart pointers should take care of the management for these members correctly.
Also for the code snippet from above, you should test against nullpointer before blindly dereferencing and calling DoSomething().
Is it possible to prevent std::vector from copying that element?
As you're asking for c++11 it's possible, but requires you never change the size of the vector, and provide a move constructor and assignment operator for CopiedClass.
Otherwise, std::vector explicitly requires copyable types (at least for certain operations):
T must meet the requirements of CopyAssignable and CopyConstructible.
It's your responsibility to meet these requirements correctly.
... it does so by copying the last element it currently holds.
Also note: In a situation the vector needs to be resized, all of the existing elements will be copied, not only the last one as you premise.
If you know the vector's maximum size in advance, then a workaround for this issue would be calling reserve with that maximum size. When the vector's capacity is big enough, no reallocations occur and no copies of existing elements need to be made.
But that's really just a workaround. You should redesign your class such that copying cannot suddenly become an invalid operation, either by making copying safe or by disallowing it and perhaps replace it with moving, like std::unique_ptr. If you make the validity of copying dependent on some run-time status, then you don't even need a std::vector to get into trouble. A simple CopiedClass get(); will be a potential error.
It seems when you try to add more elements to it, and it needs to
allocate more space, it does so by copying the last element it
currently holds. This seems to assume that any element in the vector
is fully valid, and as such a copy will always succeed.
Both sentences are not really correct. When vector runs out of space it, yes, performs reallocation, but of all the elements and these are copied to the new location or possibly moved. When you push_back or emplace_back, and reallocation is needed, the element will generally by copied: if an exception is thrown during that, it will be treated as if you had never added the element. If vector detects a noexcept user-defined move constructor ( MoveInsertable concept), elements are moved; if this constructor though is not noexcept, and an exception is thrown, the result is unspecified.
The objects have guards, but I never considered adding guards to a
copy constructor as I assumed we would never be copying an invalid
object (which vector forces) :
vector copies its value_type: it does not care whether what it contains is valid or invalid. You should take care of that in your copy control methods, whose scope is exactly to define how the object is passed around.
CopiedClass::CopiedClass(const CopiedClass& other)
: member(other.member)
{
member->DoSomething();
}
The obvious solution would be to check if member is valid and act thereby.
if (member.internalData.isValid())
member->DoSomething()
// acknowledge this.member's data is invalid
We don't know how member is represented, but Boost.Optional is what you may be looking for.
Is it possible to prevent std::vector from copying that element?
Reallocation is something vector is expected to commit so, no, you can't. reserv-ing space could avoid that, but maintaining code to handle that would not really be painless. Rather, prefer a container like std::forward_list/std::list.
Other solutions:
Hold a unique_ptr<decltype(member)>, but pointers are not often a real solution
Define move semantics for your object, as described in other answers.
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.