Какой распределитель используется при выделении вложенного контейнера STL?

У меня есть вопрос о классах STL и распределителях, которые, кажется, не так легко найти в Интернете. Кто-нибудь знает, какой распределитель используется во вложенном классе STL? Например:

typedef std::vector<int> myvect;

// строка ниже была отредактирована, как указано в последующих ответах / комментариях

typedef std::map<int, myvect, std::less<int>, A> mymap; //uses my custom allocator for map creation

Давайте назовем распределитель по умолчанию Dи предположим, что у меня есть собственный распределитель A,

Что бы произошло, если бы я сделал следующее:

  • Создать карту:

    mymap mapInstance;
    
  • Теперь, предполагая, что запись существует для mapInstance[0]Предположим, я вставил значение в вектор:

    mapInstance[0].push_back(999);
    

Какой распределитель используется для динамической памяти вектора mapInstance[0]?

Насколько я понимаю, что по умолчанию распределитель D используется, но я хочу подтвердить, что пользовательский распределитель A, который был передан на карту, вместо этого не используется. (Насколько я знаю, это произошло бы, если бы я использовал какой-то вариант вложенного размещения.)

Я понимаю, конечно, что метаданные / информация заголовка для mapInstance[0] выделяется с помощью пользовательского распределителя A, Что меня беспокоит, так это динамический часть памяти, то есть часть после d_dataBegin,

3

Решение

Ваш вопрос связан с Модель распределителя, Это стиль дизайна распределителя, который автоматически распространяет распределитель контейнера на элементы контейнера, так что вы можете гарантировать, что все элементы контейнера распределены из одного и того же распределителя. Подробнее об этом ниже.

Чтобы ответить на ваш вопрос:

1) Контейнеры не используют модель распределителя по умолчанию, вы должны запросить ее явно (см. scoped_allocator_adaptor ниже)

2) Ваш вложенный контейнер имеет тип std::vector<int> это означает, что он использует по умолчанию std::allocator<int> распределитель, и все экземпляры этого типа равны, поэтому ответ на ваш вопрос заключается в том, что он использует стандартный распределитель — не важно, какой, потому что каждый std::allocator<int> та же.


Остальная часть этого ответа — только мысленный эксперимент, ответ на ваш вопрос выше: vector<int> всегда использования std::allocator<int>

Теперь, если ваш вложенный тип был std::vector<int, A1<int>> где A1<int> это пользовательский распределитель, вопрос становится более интересным.

Вложенный контейнер будет использовать распределитель, с которым он построен, и вы не показали этого, потому что сказали «при условии, что запись существует для mapInstance[0]«и как эта запись создается, это то, что определяет, какой распределитель он будет использовать.

Если эта запись создана следующим образом:

mapInstance[0];

тогда запись построена по умолчанию и будет использовать конструкцию по умолчанию A1<int>,

Если эта запись создана следующим образом:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));

тогда запись будет копией v и в C ++ 03 его распределитель будет копией v.get_allocator(), но в C ++ 11 его распределитель будет копией std::allocator_traits<A1<int>>::select_on_container_copy_construction(v.get_allocator()), который может быть быть копией v.get_allocator() но может быть что-то другое (например, построенный по умолчанию A1.)

(В C ++ 03 распределитель записи не может быть изменен после того, как он создан, поэтому ответ здесь заканчивается, но в C ++ 11 его можно заменить. Я предполагаю, что мы говорим о C ++ 11 до конца этого вопроса потому что распределители не очень интересны в C ++ 03.)

Если эта запись изменена следующим образом:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance[0] = v;

тогда вектор получает копию, назначенную может быть заменить распределитель, в зависимости от значения std::allocator_traits<A1<int>>::propagate_on_container_copy_assignment::value

Если эта запись изменена следующим образом:

A1<int> a1( /* args */ );
mapInstance[0] = myvect(a1);

тогда вектор получает перемещение, которому может быть заменить распределитель, в зависимости от значения std::allocator_traits<A1<int>>::propagate_on_container_move_assignment::value

Если эта запись изменена следующим образом:

A1<int> a1( /* args */ );
myvect v(a1)
swap( mapInstance[0], v );

тогда вектор поменяется местами, что может быть заменить распределитель, в зависимости от значения std::allocator_traits<A1<int>>::propagate_on_container_swap::value


Сейчас если A является std::scoped_allocator_adaptor<A1<std::pair<const int, myvect>>> все становится еще интереснее! scoped_allocator_adaptor это, как следует из его названия, адаптер, который позволяет использовать любой тип распределителя с Модель распределителя это означает, что распределитель контейнера может быть передан дочерним элементам контейнера, его дочерним дочерним элементам и т. д. (при условии, что эти типы используют распределители и могут создаваться с ними).

По умолчанию контейнеры и распределители делают не использовать модель распределителя вам нужно использовать scoped_allocator_adaptor (или напишите свой собственный тип распределителя, который работает так же), чтобы использовать его. (А в C ++ 03 вообще нет поддержки для распределителей области действия.)

Если эта запись создана следующим образом:

mapInstance[0];

затем вместо записи, созданной по умолчанию, scoped_allocator_adaptor создаст его с копией распределителя карты, поэтому запись будет построена следующим образом myvect( A1<int>(mapInstance.get_allocator()) ),

Если эта запись создана следующим образом:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));

тогда запись будет иметь копию vданные, но не будет использовать свой распределитель, вместо этого он будет передан распределителю scoped_allocator_adaptor, так будет построено так: myvect( v, A1<int>(mapInstance.get_allocator()) ),


Если все это немного сбивает с толку, добро пожаловать в мой мир, но не волнуйтесь, в вашем случае vector<int> будут всегда использование std::allocator<int>,

10

Другие решения

Распределитель по умолчанию D используется для push_back вызов.

Фактически, распределитель по умолчанию используется для map а потому что тип контейнера указывает использовать распределитель по умолчанию. Предполагая ваш A наследуется от std::allocatorвсе, что происходит, это то, что ваш распределитель разделен на один по умолчанию, а контейнер ведет себя так же, как вы вообще не передавали экземпляр распределителя.

1