C++ named requirements: 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 для некоторого типа объекта без 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
(опционально)
|
(не указано) |
|
A::const_void_pointer
(опционально)
|
(не указано) |
|
A::value_type
|
T
|
|
A::size_type
(опционально)
|
(не указано) |
|
A::difference_type
(опционально)
|
(не указано) |
|
A::template rebind<U>::other
(опционально) [2] |
B
|
|
| Выражение | Возвращаемый тип | Требования |
|---|---|---|
| * 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 |
|
| 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 = std :: move ( a ) | ||
| A a ( std :: move ( b ) ) | Конструирует a равным предыдущему значению A ( b ) . |
|
| Идентификатор типа | Псевдоним типа | Требования |
A::is_always_equal
(optional) |
std::true_type или std::false_type или производные от них. |
|
| Выражение | Возвращаемый тип | Описание |
|---|---|---|
|
a.
select_on_container_copy_construction
(
)
(опционально) |
A
|
|
| Идентификатор типа | Псевдоним типа | Описание |
A::propagate_on_container_copy_assignment
(опционально) |
std::true_type или std::false_type или производные от них. |
|
A::propagate_on_container_move_assignment
(опционально) |
|
|
A::propagate_on_container_swap
(опционально) |
|
Примечания:
- ↑ См. также умные указатели ниже.
-
↑
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
.
Требования полноты аллокатора
Тип аллокатора
|
(начиная с C++17) |
Состоятельные и несостоятельные аллокаторы
Каждый Allocator тип является либо stateful , либо stateless . В общем случае, stateful тип аллокатора может иметь неравные значения, которые обозначают различные ресурсы памяти, тогда как stateless тип аллокатора обозначает единственный ресурс памяти.
|
Хотя пользовательские аллокаторы не обязаны быть бессостоятельными, использование аллокаторов с состоянием в стандартной библиотеке является определяемым реализацией. Использование неравных значений аллокаторов может привести к определяемым реализацией ошибкам времени выполнения или неопределенному поведению, если реализация не поддерживает такое использование. |
(until C++11) |
|
Пользовательские аллокаторы могут содержать состояние. Каждый контейнер или другой объект, учитывающий аллокаторы, хранит экземпляр предоставленного аллокатора и управляет заменой аллокатора через std::allocator_traits . |
(since C++11) |
Экземпляры типа аллокатора без состояния всегда сравниваются как равные. Типы аллокаторов без состояния обычно реализуются как пустые классы и подходят для оптимизации пустого базового класса .
|
Тип-член
|
(since C++11) |
Умные указатели
Когда тип-член
pointer
не является типом сырого указателя, его обычно называют
«сложным указателем»
. Такие указатели были введены для поддержки сегментированных архитектур памяти и сегодня используются для доступа к объектам, размещенным в адресных пространствах, отличающихся от однородного виртуального адресного пространства, доступного через сырые указатели. Примером сложного указателя является независимый от отображения адресов указатель
boost::interprocess::offset_ptr
, который позволяет размещать узловые структуры данных, такие как
std::set
, в разделяемой памяти и файлах, отображаемых в память с разными адресами в каждом процессе. Сложные указатели могут использоваться независимо от аллокатора, который их предоставил
, посредством шаблона класса
std::pointer_traits
(начиная с C++11)
.
Функция
std::to_address
может использоваться для получения сырого указателя из сложного указателя.
(начиная с C++20)
|
Использование специализированных указателей и пользовательских размеров/различных типов в стандартной библиотеке условно поддерживается. Реализации могут требовать, чтобы типы членов
|
(until C++11) |
КонцептДля определения объекта запроса std::get_allocator , определяется следующий только-для-демонстрации концепт.
Только-для-демонстрации концепт /*simple-allocator*/ определяет минимальные ограничения пригодности требования Allocator . |
(начиная с C++26) |
Стандартная библиотека
Следующие компоненты стандартной библиотеки удовлетворяют требованиям Allocator :
|
стандартный аллокатор
(шаблон класса) |
|
|
(C++11)
|
реализует многоуровневый аллокатор для многоуровневых контейнеров
(шаблон класса) |
|
(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 | 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
не требовалось создавать
объект массива в выделенной памяти |
требуется |
-
↑
Типы-члены
referenceиconst_referenceв std::allocator определены какT&иconst T&соответственно.-
Если
Tявляется ссылочным типом,referenceиconst_referenceявляются некорректными, поскольку ссылка на ссылку не может быть образована ( схлопывание ссылок было введено в C++11). -
Если
Tявляется константно-квалифицированным,referenceиconst_referenceсовпадают, и набор перегрузок address() является некорректным.
-
Если