I am currently working on a container class that uses allocators to manage the resources.I'll try to give a short version of what I currently do to resize the container. (The real one is not one-dimensional but the scheme is identical since the allocated data is contiguous.)
Everything that is not clear to me is marked [[[ x ]]].
Example code
template<typename T>
class example
// ...
Notes:
- size_type === std::allocator::size_type
- pointer === std::allocator::pointer
- _A === object of std::allocator
- _begin is a class member to the begin of the current container data ( [_begin,_end) )
- size() returns (_end - _begin)
- clear() calls _A.destroy() for all elements in [_begin,_end) and _A.deallocate(_begin,size())
- the destructor calls clear()
Source of resize(size_t):
void resize (size_type const & new_size)
{
if (new_size == 0U)
{ // we resize to zero, so we just remove all data
clear();
}
else if (new_size != size())
{ // we don't go to zero and don't remain the same size
size_type const old_size = size();
pointer new_mem(nullptr);
try
{
new_mem = _Allocate(new_size);
}
catch (std::bad_alloc e)
{
// [[[ 1 ]]]
}
size_type counter(0);
for (size_type i=0; i<new_size; ++i)
{
try
{
if (i<size())
_A.construct(new_mem + i, const_cast<const_reference>(*(_begin+i)));
// [[[ 2 ]]]
else
_A.construct(new_mem + i);
++counter;
}
catch (...) // [[[ 3 ]]]
{
// [[[ 4 ]]]
}
}
clear();
_begin = new_mem;
_end = _begin + new_size;
}
}
Questions:
[[[ 1 ]]]
Should I call clear() and re-throw here or is the destructor of the current object called anyway, if I don't catch here?
[[[ 2 ]]]
What about casting to an rvalue-reference here using const_cast() or std::move()? Would this break exception safty?
If I move construct, let's say 9 out of 10 elements, and element 10 throws something on move-construction, I'll loose 9 out of 10 objects!?
[[[ 3 ]]]
I read that catch (...)
should be avoided. Nevertheless, I don't know whether there is any other possibility.
Is there a way to avoid using a universal catch without knowing whether or what the constructor will throw at me?
[[[ 4 ]]]
I believe that the proper steps here are:
- Roll back completed constructions by calling the destructors on the range [new_memory, new_memory+counter)
- Deallocate new_mem
- Call clear()
- Re-throw
Is this correct?