Reputation:
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
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
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