Adrian
Adrian

Reputation: 10911

Could it be possible to have types with move operations that throw in containers?

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?

Upvotes: 11

Views: 352

Answers (2)

bartop
bartop

Reputation: 10315

First of all, I can hardly imagine object that acquires resources in the move operation. Think about it - unique_ptr just passes the pointer without acquiring anything, same for shared_ptr. string, vector, all the containters etc. just steal the pointers to the resources acquired earlier in default or copy constructor. I feel like throwing from move constructor is like throwing from destructor. Sure, go ahead, shoot yourself in your knee. But ok, I can accept that exceptions from this exist.

So let's move to the second point - when moving there can be a moment when actually both objects (moved from and moved to) are invalid. And to roll back from such situation would require additional 'magic' function to be called to fix one of them. So it seems the data cannot be repaired as standard does not define such a function.

Upvotes: 0

Nicol Bolas
Nicol Bolas

Reputation: 473966

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.

Upvotes: 10

Related Questions