While explaining move operations on objects with a colleague, I basically said that move operations should not throw exceptions in a container because if the move operation fails, then there is no way to bring back the original object reliably. Thinking about this more, I'm wondering if that is not correct and that if a move operation that does throw, it could revert the original object back to it's original state.
The reason for this, is that if an object can throw, then it would throw not due to copying or moving the contained objects from the old to the new address, but throw if a resource failed to be acquired. So all of the original information should still be there. If this is the case, then should the compiler not be able to reverse the operations that it did to reconstitute the original object?
It could be possible for an operation to be one way, like moving an integer, but in that case it could just terminate the application, and perhaps if the developer wanted to avoid the one way operation could use a swap method instead.
This would only be possible on default move operators, as if there are any additional logic, it may be difficult for the compiler to do a reverse partial transform.
Am I oversimplifying things? Is there something that I missed which keeps containers from moving objects without a non-throwing move constructor/operator?
You can use types with throwing moves in containers like
vector which can move their elements. However, such containers will not use throwing move operations.
Let's say you have a
vector of 10 throwing move elements. And the
vector needs to resize itself. So it moves 5 objects to the new memory, but the 6th throws. Well, that's OK; construction failed, so the assumption is that the value of the 6th object is fine. That is, whatever that type's exception guarantee is will be how things work.
But then, because the movement of one object failed,
vector needs to move the last 5 objects back to the first array, since
vector is trying to provide a strong exception guarantee. That's a problem, since the move back can itself fail.
C++ in general does not have valid answers when the process of repairing a failure itself fails. You can see that in exceptions; you can't emit an exception from a destructor that is called during the process of unwinding due to an exception failure.
std::terminate happens in this case.
The same goes for
vector. If the move back were to fail,
vector has no sane answer. As such, if
vector cannot guarantee that restoring its previous array state is
noexcept, then it will use copying, since that can provide that guarantee.