Does it make sense to use std :: uninitialized_fill () with any allocator?

Does it make sense to initialize the memory with the help std::uninitialized_fill()in the library when the allocator passed as the user argument was used to get the memory itself? I ask this because the distributor must provide its own method construct()(except for the method allocate()), the implementation of which may differ from the standard one, therefore, it is probably std::uninitialized_fill()not always appropriate in all cases.

To be precise, my doubts come from a C ++ book written by Straustup (Appendix E, Standard Library Exception Safety, Section E. 3.1), in which the author gives a possible implementation template<class T, class A> vector<T,A>::vector(size_type n, const T& val, const A& a): allocator a is used to get memory for a vector, then it is std::uninitialized_fill()used to initialize the received memory.

It also provides an implementation std::uninitialized_fill()that internally uses the standard new allocation to initialize the memory, but there is no more evidence of a construct()allocator method passed as an argument to the vector constructor.

+5
source share
3 answers

I checked the implementation of the GCC vector, it reads:

  vector(size_type __n, const value_type& __value,
         const allocator_type& __a = allocator_type())
  : _Base(__n, __a)
  { _M_fill_initialize(__n, __value); }

  ...

  _M_fill_initialize(size_type __n, const value_type& __value)
  {
    this->_M_impl._M_finish =
      std::__uninitialized_fill_n_a(this->_M_impl._M_start, __n, __value,
                                    _M_get_Tp_allocator());
  }

, , . uninitialized_fill_n_a , (. ), std::allocator.

construct std::allocator std::uninitialized_fill().

, :

1) std::allocator construct , new

2), , , , GCC std::vector . ( @Michael Burr, ++ 03, , ++ 11).

, ( std::allocator_traits<Alloc>::construct(alloc, pointer, value)).

( ) ,

i) std::uninitialized_fill , ( ) ,

ii) , .uninitialized_fill(first, last, value, alloc), , ( , ).

iii) std::unitialized_fill , ( ).


:

  template<typename _ForwardIterator, typename _Size, typename _Tp,
           typename _Allocator>
    _ForwardIterator
    __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, 
                             const _Tp& __x, _Allocator& __alloc)
    {
      _ForwardIterator __cur = __first;
      __try
        {
          typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
          for (; __n > 0; --__n, ++__cur)
            __traits::construct(__alloc, std::__addressof(*__cur), __x);
          return __cur;
        }
      __catch(...)
        {
          std::_Destroy(__first, __cur, __alloc);
          __throw_exception_again;
        }
    }

  template<typename _ForwardIterator, typename _Size, typename _Tp,
           typename _Tp2>
    inline _ForwardIterator
    __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, 
                             const _Tp& __x, allocator<_Tp2>&)
    { return std::uninitialized_fill_n(__first, __n, __x); }
+2

construct.

, ( / ) .

( ) -, . , ..

, construct ( ) , , new.

destroy construct ( destroy), , , construct destroy new .

+1

The implementation of any construct()non-default distribution function is required to influence the new placement (for each table 32, the requirements for the collector in C ++ 03).

Thus, use std::uninitialized_fill()(which is defined as executing a new placement for each element in a range) should be good - even if a custom allocator is used.

0
source

All Articles