2012-03-18 2 views
5

Имеет ли смысл инициализировать память std::uninitialized_fill() в библиотеке, когда распределитель, переданный пользователем как аргумент, использовался для получения самой памяти? Я прошу об этом, потому что распределитель должен предоставить свой собственный метод construct() (кроме метода allocate()), реализация которого может отличаться от стандартного, поэтому, вероятно, std::uninitialized_fill() не всегда подходит во всех случаях.Имеет ли смысл использовать std :: uninitialized_fill() с любым распределителем?

Если учесть, что мои сомнения исходят из книги C++, написанной Страуступом (приложение E «Безопасность исключений стандартной библиотеки», раздел E. 3.1), в которой автор дает возможную реализацию template<class T, class A> vector<T,A>::vector(size_type n, const T& val, const A& a): распределитель a используется для получения памяти для вектора, то для инициализации полученной памяти используется std::uninitialized_fill().

Он также дает реализацию std::uninitialized_fill(), которая внутренне использует новое стандартное размещение для инициализации памяти, но больше нет доказательств метода construct() распределителя, переданного в качестве аргумента векторному конструктору.

+1

Что 'станд :: initialize_fill() '? Вы имеете в виду 'std :: uninitialized_fill()'? Функция 'initialize_fill()' не отображается нигде в стандарте C++, в отличие от 'uninitialized_fill()'. –

+0

@ Insilico извините, это была опечатка. Спасибо. Я задал вопрос. – Martin

+2

Это похоже на мой вопрос: http://stackoverflow.com/questions/9727556/is-uninitialized-copy-fillin-first-in-last-for-dest-aa-an-oversight-inthth, я делаю подумайте, что на самом деле, вероятно, безопасно использовать обычный 'uninitialized_fill' –

ответ

2

Я проверил реализацию НКУ вектора, он читает:

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 «s construct s имеет тот же эффект, как и размещение нового и

2), которая не может быть принята для общих распределителей, или по крайней мере НКУ std::vector не предполагает, что. (Согласно @Michael Burr, вы можете предположить это на C++ 03, к сожалению, он не упоминает C++ 11).

поэтому использование конструкция (std::allocator_traits<Alloc>::construct(alloc, pointer, value)).

Мое личное мнение (после изучения этого), что

я) 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); } 
0

Реализация функции нестандартного распределителя construct() необходима для изменения нового места размещения (для каждой таблицы 32 - требования к сборщику в C++ 03).

Таким образом, использование std::uninitialized_fill() (которое определяется как выполнение нового размещения для каждого элемента в диапазоне) должно быть хорошим - даже если используется пользовательский распределитель.

+2

Я думаю, он спрашивает, нормально ли * заменить * вызов 'construct' с' uninitialized_fill', который отличается. 'construct' может иметь дополнительные эффекты, выходящие за рамки требования. – Potatoswatter

1

Я бы предпочел позвонить construct.

Существуют различные причины предоставления пользовательского распределителя, и лучшая политика распределения (с использованием пула/хранилища стека) не является единственной.

Я также видел (и пробовал) отладочные распределители, чтобы помочь отслеживать использование некорректных распределителей в пользовательских контейнерах. Также можно использовать распределители для отслеживания использования памяти, сбора статистики и т. Д.

Таким образом, construct может (или не иметь) дополнительные эффекты, помимо конструкции объекта, и, следовательно, не обязательно эквивалентен простое размещение new.

В моих отладках распределителя, называя destroy без заехав construct по этому адресу, прежде чем была ошибка (а также рекультивация памяти, не позвав destroy), так что, по крайней мере, я рекомендовал бы быть последовательным и спаривание construct с destroy и размещением new с явным вызовом деструктора.

+0

'destroy', а не' destruct' (укушенный этим один раз, в отличие от французского, он асимметричен :). –

+0

@AlexandreC .: спасибо, это то, что я получаю за то, что не проверяю ... –