Spec-Zone.ru › C++

Именованные требования C++: Allocator

Инкапсулирует стратегии доступа/адресации, выделения/освобождения памяти и конструирования/уничтожения объектов.

Каждый компонент стандартной библиотеки, которому может потребоваться выделить или освободить память, от std::string, std::vector и каждого контейнера, кроме std::array(начиная с C++11) и std::inplace_vector(начиная с C++26), до std::shared_ptr и std::function(до C++17), делает это через Allocator: объект типа класса, удовлетворяющего следующим требованиям.

Реализация многих требований к аллокатору необязательна, поскольку все AllocatorAwareContainer обращаются к аллокаторам косвенно через std::allocator_traits, а std::allocator_traits предоставляет реализацию этих требований по умолчанию.

Требования

Пусть заданы

  • T, неконстантный тип, не являющийся ссылочным(до C++11)неконстантный объектный тип(начиная с C++11)(до C++17)объектный тип без cv-квалификаторов(начиная с C++17),
  • A, тип Allocator для типа T,
  • a, объект типа A,
  • B, соответствующий тип Allocator для некоторого объектного типа U без cv-квалификаторов (полученный посредством переназначения A),
  • b, объект типа B,
  • p, значение типа std::allocator_traits<A>::pointer, полученное вызовом std::allocator_traits<A>::allocate(),
  • cp, значение типа std::allocator_traits<A>::const_pointer, полученное преобразованием из p,
  • vp, значение типа std::allocator_traits<A>::void_pointer, полученное преобразованием из p,
  • cvp, значение типа std::allocator_traits<A>::const_void_pointer, полученное преобразованием из cp или из vp,
  • xp, разыменовываемый указатель на некоторый объектный тип X без cv-квалификаторов,
  • r, lvalue типа T, полученный выражением *p,
  • n, значение типа std::allocator_traits<A>::size_type.
Внутренние типы
Идентификатор типа Тип-алиас Требования
A::pointer (необязательно) (не указано)[1]
  • Удовлетворяет требованиям NullablePointer, LegacyRandomAccessIterator и LegacyContiguousIterator.
A::const_pointer (необязательно) (не указано)
  • Удовлетворяет требованиям NullablePointer, LegacyRandomAccessIterator и LegacyContiguousIterator.
  • A::pointer преобразуем в A::const_pointer.
A::void_pointer (необязательно) (не указано)
  • Удовлетворяет требованиям NullablePointer.
  • A::pointer преобразуем в A::void_pointer.
  • B::void_pointer и A::void_pointer являются одним и тем же типом.
A::const_void_pointer (необязательно) (не указано)
  • Удовлетворяет требованиям NullablePointer.
  • A::pointer, A::const_pointer и A::void_pointer преобразуемы в A::const_void_pointer.
  • B::const_void_pointer и A::const_void_pointer являются одним и тем же типом.
A::value_type T
A::size_type (необязательно) (не указано)
  • Беззнаковый целочисленный тип.
  • Может представлять размер самого большого объекта, который A может выделить.
A::difference_type (необязательно) (не указано)
  • Знаковый целочисленный тип.
  • Может представлять разность любых двух указателей на объекты, выделенные посредством A.
A::template rebind<U>::other
(необязательно)[2]
B
  • Для любого U, B::template rebind<T>::other является A.
Операции с указателями
Выражение Тип возвращаемого значения Требования
*p T&
*cp constT& *cp и *p указывают на один и тот же объект.
p->m (как есть) То же, что (*p).m, если (*p).m определено.
cp->m (как есть) То же, что (*cp).m, если (*cp).m определено.
static_cast<A::pointer>(vp) (как есть) static_cast<A::pointer>(vp) == p
static_cast<A::const_pointer>(cvp) (как есть) static_cast<A::const_pointer>(cvp) == cp
std::pointer_traits<A::pointer>::pointer_to(r) (как есть)
Операции с памятью и временем жизни
Выражение Тип возвращаемого значения Требования
a.allocate(n) A::pointer Выделяет память, подходящую для массива объектов типа T[n], и создает массив, но не конструирует его элементы. Может выбрасывать исключения. Если n == 0, возвращаемое значение не определено.
a.allocate(n, cvp) (необязательно) То же, что a.allocate(n), но может неопределенным образом использовать cvp (nullptr или указатель, полученный из a.allocate()) для повышения локальности.
a.allocate_at_least(n) (необязательно) (начиная с C++23) std::allocation_result <A::pointer> Выделяет память, подходящую для массива объектов типа T[cnt], и создает массив, но не конструирует его элементы, а затем возвращает {p, cnt}, где p указывает на память, а cnt не меньше n. Может выбрасывать исключения.
a.deallocate(p, n) (не используется) Освобождает память, на которую указывает p; это значение должно быть возвращено предыдущим вызовом allocate или allocate_at_least(начиная с C++23), не аннулированным промежуточным вызовом deallocate. n должно совпадать со значением, ранее переданным в allocate, либо находиться между запрошенным и возвращенным количеством элементов через allocate_at_least (может совпадать с любой из границ)(начиная с C++23). Не выбрасывает исключений.
a.max_size() (необязательно) A::size_type Наибольшее значение, которое можно передать в A::allocate().
a.construct(xp, args...) (необязательно) (не используется) Конструирует объект типа X в ранее выделенной памяти по адресу, на который указывает xp, используя args... в качестве аргументов конструктора.
a.destroy(xp) (необязательно) (не используется) Уничтожает объект типа X, на который указывает xp, но не освобождает память.
Связь между экземплярами
Выражение Тип возвращаемого значения Требования
a1 == a2 bool
  • true только в том случае, если память, выделенная аллокатором a1, может быть освобождена посредством a2.
  • Устанавливает рефлексивное, симметричное и транзитивное отношение.
  • Не выбрасывает исключений.
a1 != a2
  • То же, что !(a1 == a2).
Объявление Эффект Требования
A a1(a) Копирующим конструированием создает a1 так, что a1 == a.
(Примечание: каждый Allocator также удовлетворяет требованиям CopyConstructible.)
  • Не выбрасывает исключений.
A a1 = a
A a(b) Конструирует a так, что B(a) == b и A(b) == a.
(Примечание: это означает, что все аллокаторы, связанные отношением rebind, сохраняют ресурсы друг друга, например пулы памяти.)
  • Не выбрасывает исключений.
A a1(std::move(a)) Конструирует a1 так, чтобы оно было равно предыдущему значению a.
  • Не выбрасывает исключений.
  • Значение a не изменяется и a1 == a.
A a1 = std::move(a)
A a(std::move(b)) Конструирует a так, чтобы оно было равно предыдущему значению A(b).
  • Не выбрасывает исключений.
Идентификатор типа Тип-алиас Требования
A::is_always_equal
(необязательно)
std::true_type или std::false_type либо тип, производный от такого типа.
  • true, если любые два аллокатора типа A всегда сравниваются как равные.
  • (Если не задано, std::allocator_traits по умолчанию устанавливает это значение в std::is_empty<A>::type.)
Влияние на операции контейнеров
Выражение Тип возвращаемого значения Описание
a.select_on_container_copy_construction()
(необязательно)
A
  • Предоставляет экземпляр A для использования контейнером, копирующим конструируемым из контейнера, который в данный момент использует a.
  • (Обычно возвращает либо копию a, либо сконструированный по умолчанию A.)
Идентификатор типа Тип-алиас Описание
A::propagate_on_container_copy_assignment
(необязательно)
std::true_type или std::false_type либо тип, производный от такого типа.
  • std::true_type или тип, производный от него, если аллокатор типа A необходимо скопировать при копирующем присваивании контейнера, который его использует.
  • Если этот член является std::true_type или типом, производным от него, то A должен удовлетворять требованиям CopyAssignable, а операция копирования не должна выбрасывать исключений.
  • Обратите внимание: если аллокаторы исходного и целевого контейнеров не сравниваются как равные, при копирующем присваивании необходимо освободить память целевого контейнера с помощью старого аллокатора, а затем выделить ее с помощью нового аллокатора до копирования элементов (и аллокатора).
A::propagate_on_container_move_assignment
(необязательно)
  • std::true_type или тип, производный от него, если аллокатор типа A необходимо переместить при перемещающем присваивании контейнера, который его использует.
  • Если этот член является std::true_type или типом, производным от него, то A должен удовлетворять требованиям MoveAssignable, а операция перемещения не должна выбрасывать исключений.
  • Если этот член не задан и не является типом, производным от std::false_type, а аллокаторы исходного и целевого контейнеров не сравниваются как равные, перемещающее присваивание не может принять во владение память исходного контейнера и должно по отдельности перемещающе присвоить или перемещающе сконструировать элементы, при необходимости изменяя размер собственной памяти.
A::propagate_on_container_swap
(необязательно)
  • std::true_type или тип, производный от него, если аллокаторы типа A необходимо поменять местами при обмене двух использующих их контейнеров.
  • Если этот член является std::true_type или типом, производным от него, тип A должен удовлетворять требованиям Swappable, а операция обмена не должна выбрасывать исключений.
  • Если этот член не задан и не является типом, производным от std::false_type, а аллокаторы двух контейнеров не сравниваются как равные, поведение при обмене контейнеров не определено.

Примечания:

  1. См. также раздел указатели с дополнительной функциональностью ниже.
  2. rebind является необязательным (предоставляется std::allocator_traits) только в том случае, если этот аллокатор представляет собой шаблон вида SomeAllocator<T, Args>, где Args — ноль или более дополнительных параметров типа шаблона.

Пусть заданы

  • x1 и x2, объекты (возможно, разных) типов X::void_pointer, X::const_void_pointer, X::pointer или X::const_pointer
Тогда x1 и x2 являются эквивалентными по значению значениями указателей тогда и только тогда, когда оба значения x1 и x2 можно явно преобразовать в два соответствующих объекта px1 и px2 типа X::const_pointer, используя последовательность преобразований static_cast, в которой применяются только эти четыре типа, и выражение px1 == px2 вычисляется в true.

Пусть заданы

  • w1 и w2, объекты типа X::void_pointer
Тогда в выражении w1 == w2 и w1 != w2 один или оба объекта можно заменить объектом типа X::const_void_pointer, эквивалентным по значению, без изменения семантики.

Пусть заданы

  • p1 и p2, объекты типа X::pointer
Тогда в выражениях p1 == p2, p1 != p2, p1 < p2, p1 <= p2, p1 >= p2, p1 > p2, p1 - p2 один или оба объекта можно заменить объектом типа X::const_pointer, эквивалентным по значению, без изменения семантики.

Приведенные выше требования позволяют сравнивать iterator и const_iterator контейнера Container.

Требования полноты аллокатора

Тип аллокатора X для типа T дополнительно удовлетворяет требованиям полноты аллокатора, если выполняются оба следующих условия, независимо от того, является ли T полным типом:

  • X является полным типом.
  • За исключением value_type, все типы-члены std::allocator_traits<X> являются полными типами.
(начиная с C++17)

Состояние-содержащие и не содержащие состояние аллокаторы

Каждый тип Allocator либо содержит состояние, либо не содержит состояния. Как правило, тип аллокатора, содержащий состояние, может иметь неравные значения, обозначающие различные ресурсы памяти, тогда как тип аллокатора без состояния обозначает единственный ресурс памяти.

Хотя пользовательские аллокаторы не обязаны быть аллокаторами без состояния, вопрос о том, поддерживается ли использование аллокаторов, содержащих состояние, в стандартной библиотеке и каким образом, определяется реализацией. Использование неравных значений аллокатора может привести к ошибкам времени выполнения, определяемым реализацией, или к неопределенному поведению, если реализация не поддерживает такое использование.

(до C++11)

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

(начиная с C++11)

Экземпляры типа аллокатора без состояния всегда сравниваются как равные. Типы аллокаторов без состояния обычно реализуются как пустые классы и подходят для оптимизации пустого базового класса.

Тип-член is_always_equal у std::allocator_traits предназначен для определения того, является ли тип аллокатора не содержащим состояния.

(начиная с C++11)

Указатели с дополнительной функциональностью

Если тип-член pointer не является обычным указателем, его обычно называют «указателем с дополнительной функциональностью». Такие указатели появились для поддержки сегментированных архитектур памяти и сегодня используются для доступа к объектам, размещенным в адресных пространствах, отличных от однородного виртуального адресного пространства, доступного обычным указателям. Пример такого указателя — отображаемый адресонезависимый указатель boost::interprocess::offset_ptr, позволяющий размещать структуры данных на основе узлов, такие как std::set, в общей памяти и файлах, отображаемых в память по разным адресам в каждом процессе. Указатели с дополнительной функциональностью можно использовать независимо от предоставившего их аллокаторас помощью шаблона класса std::pointer_traits(начиная с C++11). Функцию std::to_address можно использовать для получения обычного указателя из указателя с дополнительной функциональностью.(начиная с C++20)

Использование указателей с дополнительной функциональностью, а также настраиваемых размеров и отличающихся типов в стандартной библиотеке поддерживается условно. Реализации могут требовать, чтобы типы-члены pointer, const_pointer, size_type и difference_type были соответственно value_type*, const value_type*, std::size_t и std::ptrdiff_t.

(до C++11)

Концепт

Для определения объекта-запроса std::get_allocator задается следующий концепт, используемый только для описания.

template<class Alloc>
concept /*simple-allocator*/ = requires(Alloc alloc, std::size_t n)
{
    { *alloc.allocate(n) } -> std::same_as<typename Alloc::value_type&>;
    { alloc.deallocate(alloc.allocate(n), n) };  
} && std::copy_constructible<Alloc>
  && std::equality_comparable<Alloc>;

Концепт /*simple-allocator*/, используемый только для описания, задает минимальные ограничения на использование требования Allocator.

(начиная с C++26)

Стандартная библиотека

Следующие компоненты стандартной библиотеки удовлетворяют требованиям Allocator:

allocator
аллокатор по умолчанию
(шаблон класса)
scoped_allocator_adaptor
(C++11)
реализует многоуровневый аллокатор для многоуровневых контейнеров
(шаблон класса)
polymorphic_allocator
(C++17)
аллокатор, поддерживающий полиморфизм времени выполнения на основе std::pmr::memory_resource, с которым он был сконструирован
(шаблон класса)

Примеры

Демонстрирует аллокатор C++11, за исключением [[nodiscard]], добавленного для соответствия стилю C++20.

#include <cstdlib>
#include <iostream>
#include <limits>
#include <new>
#include <vector>

template<class T>
struct Mallocator
{
    typedef T value_type;
    
    Mallocator() = default;
    
    template<class U>
    constexpr Mallocator(const Mallocator <U>&) noexcept {}
    
    [[nodiscard]] T* allocate(std::size_t n)
    {
        if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
            throw std::bad_array_new_length();
        
        if (auto p = static_cast<T*>(std::malloc(n * sizeof(T))))
        {
            report(p, n);
            return p;
        }
        
        throw std::bad_alloc();
    }
    
    void deallocate(T* p, std::size_t n) noexcept
    {
        report(p, n, 0);
        std::free(p);
    }
private:
    void report(T* p, std::size_t n, bool alloc = true) const
    {
        std::cout << (alloc ? "Alloc: " : "Dealloc: ") << sizeof(T) * n
                  << " bytes at " << std::hex << std::showbase
                  << reinterpret_cast<void*>(p) << std::dec << '\n';
    }
};

template<class T, class U>
bool operator==(const Mallocator <T>&, const Mallocator <U>&) { return true; }

template<class T, class U>
bool operator!=(const Mallocator <T>&, const Mallocator <U>&) { return false; }

int main()
{
    std::vector<int, Mallocator<int>> v(8);
    v.push_back(42);
}

Возможный вывод:

Alloc: 32 bytes at 0x2020c20
Alloc: 64 bytes at 0x2023c60
Dealloc: 32 bytes at 0x2020c20
Dealloc: 64 bytes at 0x2023c60

Отчеты об ошибках

Следующие отчеты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Опубликованное поведение Корректное поведение
LWG 179 C++98 pointer и const_pointer не требовалось
сравнивать друг с другом
требуется
LWG 199 C++98 возвращаемое значение a.allocate(0) было неясным оно не определено
LWG 258
(N2436)
C++98 отношение равенства между аллокаторами не требовалось
делать рефлексивным, симметричным или транзитивным
требуется рефлексивность,
симметричность и транзитивность
LWG 274 C++98 T мог быть типом с квалификатором const или ссылочным типом,
из-за чего std::allocator мог быть некорректным[1]
такие типы запрещены
LWG 2016 C++11 операции копирования, перемещения и обмена
аллокатора могли выбрасывать исключения при использовании
требуется отсутствие исключений
LWG 2081 C++98
C++11
аллокаторы не обязаны были поддерживать копирующее
присваивание (C++98) и перемещающее присваивание (C++11)
требуется
LWG 2108 C++11 не существовало способа показать, что аллокатор не содержит состояния is_always_equal добавлен
LWG 2263 C++11 решение вопроса LWG 179 было случайно исключено из C++11
и не было обобщено для void_pointer и const_void_pointer
восстановлено и обобщено
LWG 2447 C++11 T мог быть объектным типом с квалификатором volatile такие типы запрещены
LWG 2593 C++11 перемещение из аллокатора могло изменить его значение изменение запрещено
P0593R6 C++98 allocate не обязаны были создавать
объект массива в выделенной ими памяти
требуется
  1. Типы-члены reference и const_reference у std::allocator определены соответственно как T& и const T&.
    • Если T является ссылочным типом, reference и const_reference некорректны, поскольку невозможно образовать ссылку на ссылку (схлопывание ссылок было введено в C++11; см. раздел схлопывание ссылок).
    • Если T имеет квалификатор const, reference и const_reference совпадают, а набор перегрузок address() некорректен.

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/named_req/Allocator

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API