Spec-Zone.ru › C++

Требования к 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 для некоторого типа объекта без квалификаторов 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, указатель, ссылающийся на некоторый объект типа X без квалификаторов cv,
  • r, значение типа 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 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
  • true только если память, выделенная аллокатором a1, может быть освобождена с помощью a2.
  • Устанавливает рефлексивное, симметричное и транзитивное отношение.
  • Не генерирует исключения.
a1 != a2
  • То же, что и !(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 == 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. См. также указатели fancy ниже.
  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.

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

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

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

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

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

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

(до C++11)

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

(с C++11)

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

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

(с C++17)

Указатели fancy

Когда тип члена 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)

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

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

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 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

Spec-Zone.ru

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