2016-12-18 2 views
11

Из того, что я читал в http://en.cppreference.com/w/cpp/memory/allocator, большинство функций распределителей теперь будут устаревшими. Вопрос в том, как следует использовать распределители в новом коде? Каков сейчас «правильный» способ?Как использовать распределители в современном C++

Из того, что я вывел в документации, construct является частью признаков распределителя, а не самого распределителя.

Я создаю пользовательский контейнер, здесь это очень простая версия конструктора, это хорошее использование нового дизайна?

container::container(std::size_t size, T const& value, Allocator const& allocator) : allocator_(allocator){ 
    data_ = std::allocator_traits<Alloc>::allocate(allocator_, size); 
    for(auto ptr = data_; ptr != data_ + size; ++ptr){ 
     std::allocator_traits<Allocator>::construct(allocator_, ptr, value) 
    } 
} 

Я пытался использовать алгоритм (как std::for_each) в цикле, но мне не удалось использовать один без учета адресов (operator&).

Где я могу найти полный пример современного распределителя?


После некоторой настройки, я нашел способ использовать алгоритм вместо исходного контура (к которому политика выполнения может быть передана). Я не очень уверен, но это может быть такой:

data_ = std::allocator_traits<Allocator>::allocate(allocator_, size); 
    std::for_each([policy? deduced from allocator?,] 
     boost::make_counting_iterator(data_), 
     boost::make_counting_iterator(data_ + size), 
     [&](auto ptr){std::allocator_traits<Allocator>::construct(allocator_, ptr, value);} 
    ); 
+0

Это был правильный способ сделать это с C++ 11 (по модулю сломанного состояния цикла 'for'). C++ 17 ничего не меняет, кроме как обесценивать кучу функций, которые вы все равно не должны вызывать. Кроме того, нет 'construct_n'. –

+0

"* Из того, что я читал в http://en.cppreference.com/w/cpp/memory/allocator, большинство функций распределителей теперь будут удалены и устарели. *« Исправление: это не то, что »устарело " означает. Устаревшее не означает «разделение». Это означает «подлежащее удалению в * более поздней версии». Он еще никуда не денется. –

+0

@ T.C., Я исправил код (цикл).Каким будет правильный способ написать цикл (например, можно дать политику выполнения, 'std :: for_each'? (Из чего). – alfC

ответ

3

Да, нынешний подход через std::allocator_traits. Таким образом, вы сможете поддерживать «минимальный интерфейс распределителя».

http://en.cppreference.com/w/cpp/concept/Allocator

Некоторые требования не являются обязательными: шаблон std::allocator_traits обеспечивает реализацию по умолчанию для всех дополнительных требований, а также все стандартные библиотечные контейнеры и другие распределителей-зависимых классов доступа аллокатора через std::allocator_traits, а не напрямую.

Если вы наблюдаете std::allocator_traits функции членов и определения типов, вы увидите, что они обнаружение присутствия соответствующих функций/типов и диспетчеризации через них, если они могут.

Неудача и возможное удаление в будущем ничего не изменят, если вы уже используете std::allocator_traits, поскольку он применяется только к std::allocator и их функциям-членам/typedefs.

Теперь, если вы спросите меня, нет ничего плохого в for-loops, и использование std::for_each ничего не принесет вам. Есть несколько функций uninitialized_*, но они используют прямое размещение. Если вам действительно интересно, вы можете извлечь этот код в отдельную функцию construct_range.

Существует также проблема безопасности при исключении - в случае, если один из конструкторов бросает, вам нужно уничтожить более ранние элементы, чтобы удовлетворить сильную гарантию исключения и освободить память (деструктор не будет вызван в случае, если конструктор выбрасывает)

+0

Я искал замену необработанного цикла, поэтому в будущем можно выполнить политики выполнения. Как бы вы заменили необработанный цикл на 'unitialiazed_ *' (какой?) – alfC

+0

@alfC Как упоминалось в ответ, 'uninitialized_ *' (здесь мы в значительной степени делаем то, что делают 'uninitialized_fill_n') функции используют прямое размещение new, а не' std :: allocator_traits :: construct', поэтому они не отправляют на заказ постройте функции.Теперь, если вы планируете использовать политики выполнения, это может быть на самом деле хорошо, потому что вы не можете быть уверены в том, что функции-члены распределителей являются потокобезопасными или нет, и бросают ли они исключения. – milleniumbug

+0

ok, так 'unitialized_ *' действительно не должны использоваться здесь, потому что они ничего не знают о распределителе. Это заставляет меня использовать 'std :: for_each', но это меня сбивает с толку, потому что я буду делать' std :: for_each ([policy,] data_, data_ + size, [&] (auto && e) {std :: allocator_traits :: construct (allocator_, & e, value)}) ', который звучит подозрительно. – alfC

Смежные вопросы