Skip to content

Reply to Twitter Conversation on Move

Sean Parent edited this page May 30, 2014 · 4 revisions
  1. Move is inherently an unsafe operation - it violates object invariants and that's okay. The only operations you can rely on, in general, after a move is assign-to, and destruct.

  2. std::move is inefficient - to illustrate I showed a destructive move, which does gain up to about a 15% performance advantage over the existing std::move.

The disagreement with Eric was over the semantics of (1) - should the requirements of move() be allowed to violate the invariants of the class, leaving the moved from object in a partially formed state (see EoP for definition). Or must move() leave the invariants intact somewhat a semantic argument over invariants, which I define to mean "valid representation of a value" - if you view my definition as "weakening" the invariants, you still have to define how far they can be relaxed - the algorithmic requirement is "you must be able to assign to and destruct the moved from object."

Here is what the standard says about the state of a moved from object (Table 20):

rv’s state is unspecified [ Note: rv must still meet the requirements of the library component that is using it. The operations listed in those requirements must work as specified whether rv has been moved from or not. — end note ]

So let's look at the operations that the library component could require:

copyable - but that would mean the library is copying an unspecified value. There can be no purpose for this operation, it is inefficient.

equality-comparable - the value is unspecified so likely the result will be "false", no need to compare and it would be a nonsensical comparison.

The same arguments hold for any operation that reads the value. There is not a single place in all of STL that requires, or for which I could imagine any advantage, an operation on a moved from value other than assigning to it, and destructing it.

Further, since the requirements of an algorithm are not the same as the domain requirements on a type, there is no possible way that one can satisfy said requirements with an unspecified value. For example, let's say that I had a type like so:

struct double_t
{
   double_t(const double_t&) = default;
   double_t(double_t&& x) noexcept : m_(x) { x = nan(""); }
   double_t& operator=(const double_t&) = default;
   double_t& operator=(double_t&& x) noexcept { m_ = x; x = nan(""); }

   inline friend bool operator<(const double_t& x, const double_t& y) { return x < y; }

   double m_;
};

If I were to pass a range of double_t to std::sort(), then a moved from value would violate the requirements of the algorithm. but there is no way for me to know the domain requirements of the algorithm I'm going to use on an object when deciding what the value is for my moved from state. Perhaps 0.0 is bad for an algorithm relying on divide. It simply is not possible to satisfy this requirement. Therefore, the requirement is incorrect.

QED

So regardless of what the standard says about the moved from value, if one is to trust that the standard library implementation is correct and efficient (to the extent possible without a destructive move) there is no reason to meet this requirement.

Regarding (2): I do not advocate for a destructive move, because it would add a fair amount of complexity to the language, even in the simplest form I can imagine, for a marginal win. I would rather see any complexity added to the language go toward lighting up the rest of the machine (SIMD and GPGPU support). See my 2012 C++ now talk. The point of about std::move() was to illustrate what an efficient basis operation is. That said, I do think we could have had a destructive-move in C++11 instead of r-value references and would have been generally better off for it. The only change to the language would have been specifying when RVO was required, and allowing the lifetime of an object to be explicitly ended with move(). But that ship has long since sailed.

Regarding N4034: Crash on double destruction is okay - as I've said, move is inherently unsafe. I see no possible way that it can be entirely safe and efficient (though I think we could do better than N4034).

Sean

Clone this wiki locally