Требования к C++: Класс Allocator
Описывает стратегии доступа/адресации, выделения/освобождения памяти и создания/удаления объектов.
Каждый компонент стандартной библиотеки, которому может потребоваться выделить или освободить память, от std::string, std::vector, и каждого контейнера, кроме std::array, до std::shared_ptr и std::function(до C++17), делает это с помощью Allocator: объекта класса, удовлетворяющего следующим требованиям.
Реализация многих требований Allocator необязательна, поскольку все классы, использующие Allocator, включая контейнеры стандартной библиотеки, косвенно обращаются к Allocator через std::allocator_traits, и std::allocator_traits предоставляет стандартную реализацию этих требований.
Требования
Дано
-
T, тип неконстантного, нессылочного типа(до C++11)неконстантного типа объекта(с C++11)(до C++17)типа объекта без квалификаторов cv(с C++17), -
A, тип Allocator для типаT, -
a, объект типаA, -
B, соответствующий тип Allocator для некоторого типа объекта без квалификаторов cvU(полученный путем повторной привязки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, значение типа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) (необязательно) (с 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 |
|
a1 != a2 |
|
|
| Объявление | Эффект | Требования |
A a1(a) | Копирует a1 таким образом, что a1 == a.(Примечание: Каждый аллокатор также удовлетворяет 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(необязательно) |
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(необязательно) |
|
Примечания:
- См. также указатели fancy ниже.
-
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.
Требования полноты аллокаторовТип аллокатора
| (с C++17) |
Состоятельные и бессостоятельные аллокаторы
Каждый тип аллокатора является либо состоятельным, либо бессостоятельным. Как правило, состоятельный тип аллокатора может иметь разные значения, обозначающие разные ресурсы памяти, в то время как бессостоятельный аллокатор обозначает один ресурс памяти.
| Хотя пользовательские аллокаторы не обязаны быть бессостоятельными, использование состоятельных аллокаторов в стандартной библиотеке является реализационно-зависимым. Использование различных значений аллокаторов может привести к реализационно-зависимым ошибкам выполнения или неопределённому поведению, если реализация не поддерживает такое использование. | (до C++11) |
| Пользовательские аллокаторы могут содержать состояние. Каждый контейнер или другой объект, использующий аллокаторы, хранит экземпляр предоставленного аллокатора и управляет заменой аллокаторов с помощью | (с C++11) |
Экземпляры бессостоятельного типа аллокатора всегда равны. Бессостоятельные типы аллокаторов обычно реализуются как пустые классы и подходят для оптимизации пустого базового класса.
| Тип-член | (с C++17) |
Указатели fancy
Когда тип члена pointer не является типом сырой указатель, он обычно называется "указателем специального типа". Такие указатели были введены для поддержки архитектур сегментированной памяти и используются сегодня для доступа к объектам, выделенным в адресных пространствах, отличных от однородного виртуального адресного пространства, к которому обращаются сырые указатели. Примером указателя специального типа является независимый от адреса отображения указатель boost::interprocess::offset_ptr, который позволяет выделять структуры данных на основе узлов, такие как std::set в общей памяти и памяти, отображаемой в файлы, отображаемые по разным адресам в каждом процессе. Указатели специального типа могут использоваться независимо от выделенного для них аллокатора, через шаблон класса std::pointer_traits(с C++11). Функция std::to_address может использоваться для получения сырого указателя из указателя специального типа.(с C++20)
| Использование указателей специального типа и настраиваемого размера/различного типа в стандартной библиотеке поддерживается условно. Реализации могут требовать, чтобы тип члена | (до C++11) |
Стандартная библиотека
Следующие компоненты стандартной библиотеки удовлетворяют требованиям аллокатора:
| по умолчанию (шаблон класса) |
|
|
(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 | Применено к | Поведение при публикации | Корректное поведение |
|---|---|---|---|
| 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 не требовалось создавать объект массива в памяти, которую он выделил | требовалось |
- Типы членов
referenceиconst T&от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/w/cpp/named_req/Allocator