Но как мне узнать, какой тип распределителя использует контейнер, если он внутренне перепривязывает данный распределитель из параметра шаблона?
Всегда указывайте конструктору Allocator<T>
(где T
— это value_type
контейнера). Контейнер преобразует его в Allocator<U>
необходимо, где U
— некоторая внутренняя структура данных контейнера. Allocator
требуется для предоставления таких конструкторов преобразования, например:
template <class T> class allocator {
...
template <class U> allocator(const allocator<U>&);
Кроме того, я читал, что С++ 11 теперь использует распределители с областью действия, которые позволяют повторно использовать распределитель контейнера для содержащих его контейнеров.
Точнее, в C++11 есть адаптер распределителя с именем scoped_allocator_adaptor
:
template <class OuterAlloc, class... InnerAllocs>
class scoped_allocator_adaptor : public OuterAlloc
{
...
};
Из С++ 11:
Шаблон класса scoped_allocator_adaptor
— это шаблон распределителя, который указывает ресурс памяти (внешний распределитель), который будет использоваться контейнером (как это делает любой другой распределитель), а также указывает ресурс внутреннего распределителя, который должен быть передан конструктору каждого элемента внутри контейнера. . Этот адаптер создается с одним внешним и нулем или более внутренними типами распределителя. Если создается экземпляр только с одним типом распределителя, внутренний распределитель становится самим scoped_allocator_adaptor
, таким образом, используя один и тот же ресурс распределителя для контейнера и каждого элемента внутри контейнера и, если сами элементы являются контейнерами, каждый из их элементов рекурсивно. Если создан более чем один распределитель, первый распределитель является внешним распределителем для использования контейнером, второй распределитель передается конструкторам элементов контейнера, и, если сами элементы являются контейнерами, третий распределитель передается в элементы элементы и так далее. Если контейнеры вложены на глубину, превышающую количество распределителей, последний распределитель используется повторно, как и в случае с одним распределителем, для всех оставшихся рекурсий. [Примечание: scoped_allocator_adaptor
является производным от типа внешнего распределителя, поэтому его можно заменить на тип внешнего распределителя в большинстве выражений. — конец примечания ]
Таким образом, вы получаете поведение распределителей с ограниченной областью действия только в том случае, если вы укажете scoped_allocator_adaptor
в качестве распределителя для своего контейнера.
Чем реализация контейнера с включенным распределителем области действия примерно отличается от реализации, которая не поддерживает контейнеры с областью действия?
Суть в том, что теперь контейнер взаимодействует со своим распределителем через новый класс allocator_traits
, а не напрямую с распределителем. И контейнер должен использовать allocator_traits
для определенных операций, таких как создание и уничтожение value_type
в контейнере. Контейнер не должен взаимодействовать с распределителем напрямую.
Например, распределители могут предоставить элемент с именем construct
, который создаст тип по определенному адресу, используя заданные аргументы:
template <class T> class Allocator {
...
template<class U, class... Args>
void construct(U* p, Args&&... args);
};
Если распределитель не предоставляет этот элемент, allocator_traits
предоставит реализацию по умолчанию. В любом случае контейнер должен построить все value_type
, используя эту функцию construct
, но используя ее через allocator_traits
, а не используя allocator
напрямую:
allocator_traits<allocator_type>::construct(the_allocator, *ugly details*);
scoped_allocator_adaptor
предоставляет пользовательские функции construct
, которые allocator_traits
будут пересылать, чтобы воспользоваться преимуществами свойств uses_allocator
и передать правильный распределитель вместе с конструктором value_type
. Контейнер остается в блаженном неведении об этих деталях. Контейнеру нужно только знать, что он должен создать value_type
с помощью функции allocator_traits construct
.
Есть больше деталей, с которыми контейнер должен иметь дело, чтобы правильно обрабатывать распределители с отслеживанием состояния. Хотя эти детали тоже решаются за счет того, что контейнер не делает никаких предположений, а получает все свойства и поведение через allocator_traits
. Контейнер даже не может предположить, что pointer
это T*
. Скорее, этот тип можно найти, спросив allocator_traits
, что это такое.
Короче говоря, чтобы создать контейнер C++11, изучите allocator_traits
. И затем вы бесплатно получаете поведение распределителя с ограниченной областью действия, когда ваши клиенты используют scoped_allocator_adaptor
.
person
Howard Hinnant
schedule
23.09.2012