Namespaces
Variants

C++ named requirements: Allocator

From cppreference.net
C++ named requirements

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

Каждый компонент стандартной библиотеки, которому может потребоваться выделение или освобождение памяти, от 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 для некоторого типа объекта без cv-квалификаторов U (полученный перепривязкой 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 , разыменовываемый указатель на некоторый тип объекта без cv-квалификаторов X ,
  • r , lvalue типа T , полученное выражением * p ,
  • n , значение типа std:: allocator_traits < A > :: size_type .
Внутренние типы
Идентификатор типа Псевдоним типа Требования
A::pointer (опционально) (не указано) [1]
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 const T & * 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 ) (опционально) (since C++23) std:: allocation_result

< A :: pointer >

Выделяет память, подходящую для массива типа T[cnt] и создает массив, но не конструирует элементы массива, затем возвращает { p, cnt } , где p указывает на память и cnt не меньше чем n . Может генерировать исключения.
a. deallocate ( p, n ) (не используется) Освобождает память, на которую указывает p , которая должна быть значением, возвращенным предыдущим вызовом allocate или allocate_at_least (since C++23) и не была инвалидирована промежуточным вызовом deallocate . n должно соответствовать значению, ранее переданному в allocate или находиться между запрошенным и возвращенным количеством элементов через allocate_at_least (может быть равно любой из границ) (since 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
(optional)
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 без изменения семантики.

Вышеуказанные требования делают возможным сравнение Container iterator и const_iterator .

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

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

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

Состоятельные и несостоятельные аллокаторы

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

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

(until C++11)

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

(since C++11)

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

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

(since 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 соответственно.

(until 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 :

стандартный аллокатор
(шаблон класса)
реализует многоуровневый аллокатор для многоуровневых контейнеров
(шаблон класса)
аллокатор, поддерживающий полиморфизм времени выполнения на основе 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 Applied to Behavior as published Correct behavior
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 issue 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 является константно-квалифицированным, reference и const_reference совпадают, и набор перегрузок address() является некорректным.