Question

In the comments on Andrzej's move constructor article, I posted that a moved from object can have any member function called on it that does not have a pre-condition. I gave the example std::vector::front as a function that you cannot call on a moved-from std::vector because it has the pre-condition that the vector is not empty. I gave the examples of std::vector::empty, std::vector::push_back, and std::vector::reserve as functions that you can (but shouldn't) call on a moved-from std::vector, because they have no pre-conditions.

However, that got me thinking. std::vector::push_back requires that there is enough contiguous memory available on the host system. This isn't so much a requirement on the std::vector object as it is about the system it is running on, but that still seems to me to be a pre-condition.

What is the context of move constructors leaving an object in a valid but unspecified state, and does it apply to potential out-of-memory situations with std::vector::push_back? In particular, if std::vector::push_back would have worked prior to a move, is it guaranteed to work after (ignoring issues such as other processes using up memory)?

For reference: § 17.6.3.1

Table 20 — MoveConstructible requirements [moveconstructible]
Expression  Post-condition
T u = rv;   u is equivalent to the value of rv before the construction
T(rv)       T(rv) is equivalent to the value of rv before the construction
rv’s state is unspecified [ Note:rv must still meet the requirements of the library compo-
nent that is using it. The operations listed in those requirements must work as specified
whether rv has been moved from or not. — end note ]
Was it helpful?

Solution 2

What is the context of move constructors leaving an object in a valid but unspecified state, and does it apply to potential out-of-memory situations with std::vector::push_back?

No, it doesn't apply. Having enough memory to do a push_back is not a precondition. It's OK to call push_back when the system has no more memory (in general it's not possible for the program to know in advance if the allocation will succeed or not) and if there isn't enough memory you get an exception. That's just the normal behaviour of push_back not a precondition violation.

In particular, if std::vector::push_back would have worked prior to a move, is it guaranteed to work after (ignoring issues such as other processes using up memory)?

It's legal to attempt to push_back but it's not guaranteed to work. After a move the vector's storage might have been moved to the target of the move, and push_back could cause a reallocation which could fail and throw bad_alloc.

But ignoring allocation failure, the push_back will succeed, and as Howard's comment says, now your vector which previously had an unknown number of elements has an unknown number plus one. Valid but not very useful.

OTHER TIPS

If there is insufficient memory, then push_back exits with a std::bad_alloc exception and the post-condition does not apply. The vector object is left in its original state.

When a vector reaches capacity, this is what happens:

  1. Allocate a bigger block. If this throws a bad_alloc exception, pass it along to the user. Nothing was ever modified.
  2. If the type is not movable, or movable but the move constructor may throw an exception, then copy the elements of the sequence into the bigger block. If a constructor throws an exception, destroy everything already in the bigger block, then free it, then rethrow the exception.
  3. If the type is movable and the move constructor is noexcept, then move the elements into the bigger block. This must succeed. If it the noexcept spec is violated, then the implementation doesn't have to try to move things back (which could, and likely would, also fail.)
  4. Destroy the original memory block and retain the new one.

"Valid but unspecified" doesn't have any deeper meaning in this context. The user simply isn't supposed to make an assumption about what's in it. But inspecting it to discover the contents, or ignoring the contents and adding more, are fine.

Logically, any object should be left empty or in its original state. I seem to recall that vector is actually specified to be left empty, and many programmers assume so, correct or not.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top