(Why) should a move constructor or move assignment operator clear its argument?
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
.
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.
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.