std::vector allocator-extended move constructor

Jonathan Wakely jwakely.gcc@gmail.com
Fri Nov 15 12:12:00 GMT 2013


I'm finishing allocator support for <map> and <set> and noticed a
problem in std::vector:

  /// Move constructor with alternative allocator
  vector(vector&& __rv, const allocator_type& __m)
  : _Base(std::move(__rv), __m)
  {
    if (__rv.get_allocator() != __m)
      {
        this->_M_impl._M_finish =
          std::__uninitialized_move_a(__rv.begin(), __rv.end(),
                      this->_M_impl._M_start,
                      _M_get_Tp_allocator());
        __rv.clear();
      }
  }

The good news is that if all instances of the allocator type compare
equal then the _Base will just move some pointers, so we can have a
conditional noexcept:
      noexcept(_Alloc_traits::_S_always_equal())

The bad news is that it's not currently exception-safe.  I should have
used __uninitialized_copy_a there.  If moving any element throws an
exception then we leak any already-moved elements.

We could try to conditionally use moves, but we can't just check for
is_nothrow_move_constructible, because the allocator can call a
different constructor, e.g. scoped allocators will call an
allocator-extended move constructor.  So we would need a new trait
"is_nothrow_move_insertable", which would depend on
noexcept(_Alloc_traits::construct(_M_get_Tp_allocator(),
std::declval<_Tp>())) but that will only give the right answer if we
add a conditional noexcept on std::allocator_traits<A>::construct(),
and that will only give the right answer if A::construct() has a
conditional noexcept on its construct() member function (or there is
no viable A::construct().)  It might be worth doing all that and
adding conditional noexcept on std::allocator::construct() in the
future, but for now I'm just going to switch to using
__uninitialized_copy_a() to be safe.

Can anyone see any flaws in my analysis before I make that change?



More information about the Libstdc++ mailing list