polymorphic_allocator: коли і для чого я повинен його використовувати?


122

Ось документація щодо cppreference , ось робочий проект.

Я мушу визнати, що я не розумів, у чому полягає справжня мета polymorphic_allocatorі коли / чому / як я повинен її використовувати.
Наприклад, pmr::vectorпідпис має такий підпис:

namespace pmr {
    template <class T>
    using vector = std::vector<T, polymorphic_allocator<T>>;
}

Що polymorphic_allocatorпропонує пропозиція? Що також std::pmr::vectorпропонує пропозиція щодо старомодних std::vector? Що я можу зробити зараз, чого до цього не міг зробити?
Яка реальна мета цього розподільника і коли я його повинен використовувати насправді?


1
Вони намагаються подолати якісь проблеми, що allocator<T>є у них. Тож ви побачите в ньому цінність, якщо ви часто використовуєте розподільники.
edmz

2
Відповідний папір .
edmz

Відповіді:


103

Вибір із cppreference:

Цей поліморфізм часу виконання дозволяє об'єктам, що використовують polymorphic_allocator, поводитись так, ніби вони використовували різні типи алокаторів під час виконання, незважаючи на ідентичний тип статичного розподільника

Проблема з "звичайними" розподільниками полягає в тому, що вони змінюють тип контейнера. Якщо ви хочете мати vectorпевний розподільник, ви можете скористатися Allocatorпараметром шаблону:

auto my_vector = std::vector<int,my_allocator>();

Проблема зараз полягає в тому, що цей вектор не є тим самим типом, як вектор з іншим розподільником. Ви не можете передати його функції, яка вимагає, наприклад, вектора-розподільника за замовчуванням, наприклад, або призначити два вектори з іншим типом алокатора до тієї ж змінної / покажчика, наприклад:

auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error

Поліморфний алокатор - це єдиний тип алокатора з членом, який може визначати поведінку алокатора за допомогою динамічної диспетчеризації, а не через механізм шаблону. Це дозволяє мати контейнери, які використовують специфічне, індивідуальне розподілення, але все ще є загальним типом.

Налаштування поведінки алокатора здійснюється шляхом надання алокатора std::memory_resource *:

// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);

// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);

auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
      // my_vector and my_other_vector have same type

Як я бачу, головне питання, що std::pmr::контейнер все ще не сумісний з еквівалентним std::контейнером, використовуючи розподільник за замовчуванням. Вам потрібно прийняти деякі рішення під час проектування інтерфейсу, який працює з контейнером:

  • чи ймовірно, що контейнер, переданий у контейнер, може вимагати спеціального виділення?
  • якщо так, чи слід додати параметр шаблону (щоб дозволити довільні алокатори) чи маю намір використовувати поліморфний розподільник?

Рішення шаблону дозволяє використовувати будь-який розподільник, включаючи поліморфний розподільник, але має й інші недоліки (згенерований розмір коду, час компіляції, код повинен бути викритий у файлі заголовка; потенціал для подальшого "типу забруднення", який продовжує висувати проблему назовні). Рішення поліморфного алокатора, з іншого боку, наказує, що необхідно використовувати поліморфний алокатор . Це виключає використання std::контейнерів, які використовують розподільник за замовчуванням, і це може мати наслідки для взаємодії зі застарілим кодом.

Порівнюючи зі звичайним алокатором, поліморфний алокатор має деякі незначні витрати, такі як накладні витрати вказівника пам'яті_ресурсу (що, швидше за все, незначно) та вартість відправки віртуальної функції на розподіли. Насправді, головна проблема - це, мабуть, відсутність сумісності із застарілим кодом, який не використовує поліморфні розподільники.


2
Отже, чи std::pmr::велика ймовірність того , що бінарний макет для занять відрізняється?
Еурі Пінхоллоу

12
@EuriPinhollow ви не можете знаходитись reinterpret_castміж a std::vector<X>і std::pmr::vector<X>, якщо про це ви просите.
davmac

4
Для простих випадків, коли ресурс пам'яті не залежить від змінної часу виконання, хороший компілятор буде девіартуалізуватися, і ви отримаєте поліморфний розподільник без зайвих витрат (за винятком збереження покажчика, що насправді не є проблемою). Я думав, що це варто згадати.
Дейдей

1
@ Yakk-AdamNevraumont " std::pmr::контейнер все ще не сумісний з еквівалентним std::контейнером, використовуючи розподільник за замовчуванням" . Немає жодного оператора присвоєння, визначеного від одного до іншого. Якщо ви сумніваєтеся, спробуйте це: godbolt.org/z/Q5BKev (код не зовсім такий, як вище, тому що gcc / clang мають класи "поліморфного розподілу" в "експериментальному" просторі імен).
davmac

1
@davmac Так, template<class OtherA, std::enable_if< A can be constructed from OtherA > vector( vector<T, OtherA>&& )конструктора немає. Я був невпевнений і не знав, де знайти компілятор, який мав сумісний з TS pmr.
Якк - Адам Невраумон

33

polymorphic_allocatorпризначений для користувацького алокатора, як std::functionі для прямого виклику функції.

Він просто дозволяє використовувати алокатор разом із контейнером, не вирішуючи, в точці декларації, який саме. Тож якщо у вас є ситуація, коли було б доречно більше одного розподільника, ви можете використовувати polymorphic_allocator.

Можливо, ви хочете приховати, який розподільник використовується для спрощення вашого інтерфейсу, або, можливо, ви хочете мати можливість заміняти його на різні випадки виконання.

Спочатку вам потрібен код, який потребує алокатора, потім потрібно мати можливість поміняти місцями, який використовується, перш ніж розглядати вектор pmr.


7

Один недолік поліморфних розподільників - polymorphic_allocator<T>::pointerце завжди справедливий T*. Це означає, що ви не можете використовувати їх з фантазійними покажчиками . Якщо ви хочете зробити щось на зразок місця розташування елементів vectorспільної пам’яті та отримати доступ до них через boost::interprocess::offset_ptrs , вам потрібно використовувати звичайний старий неполіморфний розподільник.

Отже, хоча поліморфні алокатори дозволяють змінювати поведінку розподілу, не змінюючи статичний тип контейнера, вони обмежують, що таке виділення .


2
Це ключовий момент і великий пробій. Артур O'Dwyer - х К значущих покажчиків фантазії паперу досліджує територію, як це робить його книгу «Mastering C ++ 17 STL»
sehe

чи можете ви надати справжній випадок використання поліморфного розподільника?
darune
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.