user3624760
user3624760

Reputation:

Are allocator construct and destroy member functions allowed to throw exceptions from internal logic?

I'm writing my own container that requires nothrow movable and copyable types. I thought I could simplify logic a bit when it comes to exception safety. But I noticed that construct and destroy member functions of allocators have no wording about when they can and can't throw exceptions.

I'm pretty sure I've read the wording before. Otherwise, even if my type is nothrow movable, construct can still throw an exception from user-provided allocator when, say, I resize the buffer to increase capacity. This requires complex rollback code to guarantee strong exception safety that I really wanted to skip.

Is there a wording that allows to only throw exceptions from calling ctor/dtor or are allocators always require complex machinery to maintain exception safety?

Upvotes: 2

Views: 338

Answers (2)

Hunter Kohler
Hunter Kohler

Reputation: 2735

The omission they have made has a reason (or two). First, destructors can throw. Second, the standard wants to avoid undefined behavior in useful contexts. If they outright banned throwing destroy in allocators, then you could not have an allocator that does non-trivial work in the destroy function. Imagine in tests, you could use an allocator which tracks the allocation, deallocation, construction, and destruction of objects. In this case, destroy may throw, as there may be some container the allocator is referencing which is needed to store the data. However, a good developer should know that throwing in a destructor, or, in this case, the allocator's destroy method, will likely invalid the state of the whole program. So, you can use destroy, in almost all cases, as if it would not throw, and allow unhandled exceptions/termination if it does.

Upvotes: 0

Jonathan Wakely
Jonathan Wakely

Reputation: 171383

The allocator members can throw. If the standard doesn't say they can't throw, then they can throw.

But in practice, why would they?

Especially for std::allocator::construct and std::allocator::destroy, which do nothing more than just invoke a constructor or destructor. But you should be using std::allocator_traits<Alloc>::construct and std::allocator_traits<Alloc>::destroy anyway, and those do nothing more than call Alloc::construct / Alloc::destroy if that exists, and invoke a constructor/destructor otherwise.

In theory, a pathological allocator could throw in those members, but you should assume they don't. You can't defend against pathologically stupid types.

Upvotes: 3

Related Questions