Why doesn't moving an object leave it null? - c++

A a5(move(a1));
While after the move the member vars of a1 are set to defaults, a1 itself is not set to null. You can't do a1 == nullptr... to check if a1 is useless...
I find this odd. Is there something I'm misunderstanding here? I would think that if a1 is moved, it becomes useless, this should be indicated by setting it to null somehow. No?
The thing is that by leaving a1 in a non null state, it still can be used. There is no compiler warning or error. There is no real indication that the object is in a messed up state. if a has two member vars, an int and a dynamically alloc object, the dyn alloc object will point to nullptr but the int will have a def value (of course only if implemented right...easy to mess up).
So after the move you can
int number = a1.getInt();
and get back a number not realizing that a1 has been reset. In C and C++ we're taught to set pointers to null (a.k.a nullptr or NULL) when its resource is pilfered to eliminate such confusion. With the introduction of moving which pilfers resources of an object, is there no built in mechanism or best practice to indicate the object has been pilfered and thus left "reset" to default construction state?
EDIT
Added sample move c'tor
A(A&& other) : num(other.num), s(other.s){
other.num = 0;
other.s = nullptr; //dyn alloc obj
}

Why doesn't moving an object leave it null?
Because if the object is not a pointer, then it does not, in general, have a "null state", so you can't "leave it null"; and if it is a pointer you don't make it null when you "move" from it.
Leaving a1 in a non null state, it still can be used. There is no compiler warning or error. There is no real indication that the object is in a messed up state...
It's not in a messed-up state, it's in a valid state. But perhaps it's not a bad idea for there to be warning when you use the post-move value of a moved-from object.

The result of a move operation is supposed to leave the object in a valid but unspecified state. Typically you would move if you are going to destroy the object, or assign new values to the object. You would not normally attempt to use an object after moving out of it.
The move-constructor for a1's type is responsible for performing the move and leaving the object in the state that you wish. If the default-generated move constructor does not behave as you would like then you can write your own move constructor for the object. For example you could set a pointer to null in this move constructor. Or you could set a flag which indicates the object is in a moved-from state.
In your question the behaviour you "wish for" is more like the behaviour of swapping with an empty object. Perhaps the following code would suit you better:
A a5{};
using std::swap;
swap(a5, a1);
Regarding the update which suggests other.s points to a "dyn alloc obj": you should avoid doing this because it means you have to waste time writing a copy-constructor, move-constructor, copy-assignment operator, move-assignment operator, and destructor.
You are the architect of your own demise here: by introducing this pointer to your class you create the exact problem with moving that you are complaining about.
Instead any owned resource should be managed by its own class. The standard library's std::unique_ptr suffices for many such use cases.

Related

What is the common idiom(s) for resetting the moved object?

In C++ move constructor is required to reset the moved object which to me seems to be a duplication of what the destructor does in most of the cases.
Is it correct that defining a reset-method and using it in both destructor and move-constructor is the best approach? Or maybe there are better ways?
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 some types, such as std::unique_ptr, the moved-from state is fully specified.
"Stolen" resources should not be released as this will usually lead to errors. For example, a move constructor which "steals" a pointer has to ensure that the destructor of the moved-from object won't delete the pointer. Otherwise, there will be a double-free. A common way of implementing this is to reset the moved-from pointer to nullptr.
Here is an example:
struct Pointer {
int *ptr;
// obtain a ptr resource which we will manage
Pointer(int* ptr) : ptr{ptr} {}
// steal another object's ptr resource, assign it to nullptr
Pointer(Pointer &&moveOf) : ptr{moveOf.ptr} {
moveOf.ptr = nullptr;
}
// make sure that we don't delete a stolen ptr
~Pointer() {
if (ptr != nullptr) {
delete ptr;
}
}
};
Is it correct that defining a reset-method and using it in both destructor and move-constructor is the best approach? Or maybe there are better ways?
This depends on the resource which is managed, but typically the destructor and move-constructor do different things. The move constructor steals the resource, the destructor frees a resource if it hasn't been stolen.
In C++ move constructor is required to reset the moved object which to me seems to be a duplication of what the destructor does in most of the cases.
You are right that there often is duplication of work. This is because C++ does not have destructive move semantics, so the destructor still gets called separately, even when an object has been moved from. In the example I have shown, ~Pointer() still needs to get called, even after a move. This comes with the runtime cost of checking whether ptr == nullptr. An example of a language with destructive move semantics would be Rust.
Related Posts:
Why does C++ move semantics leave the source constructed?
How does Rust provide move semantics?
How to define a move constructor?
is required to reset the moved object which to me seems to be a duplication of what the destructor does in most of the cases.
It seems to me that you misunderstand what a destructor is used for in most cases.
The purpose of the "reset" (as you call it) in move is to set the state of the object so that it satisfies the internal pre-conditions of the destructor (more generally, any class invariant). If the constructor didn't do that, then the object couldn't be destroyed, which would be against conventions and good practices and would likely lead to mistakes.
In many cases, the destructor cannot possibly do this same "reset". For example, there is no way to distinguish an invalid pointer from a valid pointer. This is why the move constructor of a smart pointer resets the pointer to null.
Is it correct that defining a reset-method and using it in both destructor and move-constructor is the best approach?
It is unclear when this could be useful. Doesn't seem typical.

Is move semantics just a shallow copy and setting other's pointers to null?

I've been reading about move semantics in C++, and in the explanations people give a lot of analogies to help simplify it, and in my head all I can see is that what people call "moving" as opposed to "copying" is just a shallow copy of the object and setting any pointers in the "moved-from" object to null. Is this basically the gist? Shallow copy and set other's pointers to null?
Shallow copy and set other's pointers to null?
Shallow copy - yes. Setting other's pointers to null - not always.
The minimum requirement is that the moved-from object is in an "undefined but valid state", which is to say that you can reassign to it, move it again or delete it without causing the program to fail, but perform no other state-dependent operation on it. This means that it's perfectly valid in general to implement move-assignment in terms of std::swap.
Some objects define a stronger contract. One of these is std::unique_ptr. Moving-from one of these will result in it containing null, but this is explicitly documented.
Is move semantics just a shallow copy and setting other's pointers to null?
It can be, if the pointers being null satisfies the class invariant. That is to say: If the object with null pointers is a valid state.
So, I would give a longer description: Move constructor and assignment operator perform a shallow copy, and clean up the moved from object into a state that satisfies the class invariant.
Also remember that in the case of move assignment, you must remember to handle the pointer that would be overwritten by the swallow copy.
If the class owns pointed objects for example, the invariant requires that no two objects own the same object. There are at least three ways to implement this:
Set the pointer to null
Construct a new object
Swap the pointers with the moved to object (in case of move constructor, this is essentially same as one of the previous options since the pointer is initially uninitialized unless one of the previous is first performed, but with move assignment this handily takes care of the object previously pointed by the moved-to object).
It is sometimes necessary to also set non-resource data such as size field or similar to match the new state of the object if the invariant so requires. There are other resources that the object might hold and must also be cleaned up besides owning pointers to memory, for example file descriptors.

(Why) should a move constructor or move assignment operator clear its argument?

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)

why is the destructor call after the std::move necessary?

In The C++ programming language Edition 4 there is an example of a vector implementation, see relevant code at the end of the message.
uninitialized_move() initializes new T objects into the new memory area by moving them from the old memory area. Then it calls the destructor on the original T object, the moved-from object. Why is the destructor call necessary in this case?
Here is my incomplete understanding: moving an object means that the ownership of the resources owned by the moved-from object are transferred to the moved-to object. The remainings in the moved-from object are some possible members of built-in types that do not need to be destroyed, they will be deallocated when the the vector_base b goes out of scope (inside reserve(), after the swap() call). All pointers in the moved-from object are to be put to nullptr or some mechanism is employed to drop ownership of moved-from object on those resources so that we are safe, then why call the destructor on a depleted object when the "vector_base b" destructor will anyway deallocate the memory after the swap is done?
I understand the need to explicitly call the destructor in the cases when it must be called because we have something to destruct (e.g. drop elements) but I fail to see its meaning after the std::move + deallocation of vector_base. I read some texts on the net and I'm seeing the destructor call of the moved-from object as an signal (to whom or what?) that the lifetime of the object is over.
Please clarify to me what meaningful work remains to be done by the destructor? Thank you!
The code snippet below is from here http://www.stroustrup.com/4th_printing3.html
template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc)
{
if (newalloc<=capacity()) return; // never decrease allocation
vector_base<T,A> b {vb.alloc,size(),newalloc-size()}; // get new space
uninitialized_move(vb.elem,vb.elem+size(),b.elem); // move elements
swap(vb,b); // install new base
} // implicitly release old space
template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
using T = Value_type<Out>; // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_)
for (; b!=e; ++b,++oo) {
new(static_cast<void*>(&*oo)) T{move(*b)}; // move construct
b->~T(); // destroy
}
return oo;
}
Moving from an object just means that the moved-from object might donate its guts to live on in another live object shortly before it is [probably] going to die. Note, however, that just because an object donated its guts that the object isn't dead! In fact, it may be revived by another donating object and live on that object's guts.
Also, it is important to understand that move construction or move assignment can actually be copies! In fact, they will be copies if the type being moved happens to be a pre-C++11 type with a copy constructor or a copy assignment. Even if a class has a move constructor or a move assignment it may choose that it can't move its guts to the new object, e.g., because the allocators mismatch.
In any case, a moved from object may still have resources or need to record statistics or whatever. To get rid of the object it needs to be destroyed. Depending on the class's contracts it may even have a defined state after being moved from and could be put to new use without any further ado.
In his proposal to implement move semantics through rvalue references, Howard Hinant considered the push for Destructive Move too. But as he pointed in the same article, a move operation basically deals with to objects (source an destination) simultaneously in transition state - even in single threaded applications. The problem with destructive move is that either source, or destination will experience a state in where derived subpart of an object is constructed while base subpart is destroyed or uninitialized. This was not possible prior to move proposal, and is not acceptable yet. So the other choice left was to leave the moved from object in a valid empty state and let the destructor remove the valid empty state. For many practical purposes, destroying valid empty state is a noop; But it's not possible to be generalized for all cases. So, the moved from object is supposed to be either destructed, or assigned(move or copy) to.

Questions about postblit and move semantics

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.