C++ named requirements: Allocator
Encapsulates strategies for access/addressing, allocation/deallocation and construction/destruction of objects.
Every standard library component that may need to allocate or release storage, from std::string, std::vector, and every container, except std::array(since C++11) and std::inplace_vector(since C++26), to std::shared_ptr and std::function (until C++17) , does so through an Allocator
The implementation of many allocator requirements is optional because all AllocatorAwareContainer access allocators indirectly through std::allocator_traits, and std::allocator_traits
Requirements
Given
-
T, a non-const, non-reference type(until C++11) non-const object type(since C++11)(until C++17) cv-unqualified object type(since C++17) A, an Allocator type for typeT,- a, an object of type
A, B, the corresponding Allocator type for some cv-unqualified object typeU(as obtained by rebindingA),- b, an object of type
B, - p, a value of type std::allocator_traits <A> :: pointer , obtained by calling std::allocator_traits <A> :: allocate ( )
- cp, a value of type std::allocator_traits <A> :: const_pointer , obtained by conversion from p
- vp, a value of type std::allocator_traits <A> :: void_pointer , obtained by conversion from p
- cvp, a value of type std::allocator_traits <A> :: const_void_pointer , obtained by conversion from cp or from vp
- xp, a dereferenceable pointer to some cv-unqualified object type
X, - r, an lvalue of type
Tobtained by the expression *p, - n, a value of type std::allocator_traits <A> :: size_type
| Type-id | Aliased type | Requirements |
|---|---|---|
A::pointer (optional)
|
(unspecified)[1] |
|
A::const_pointer (optional)
|
(unspecified) |
|
A::void_pointer (optional)
|
(unspecified) |
|
A::const_void_pointer (optional)
|
(unspecified) |
|
A::value_type
|
T
|
|
A::size_type (optional)
|
(unspecified) |
|
A::difference_type (optional)
|
(unspecified) |
|
A::template rebind<U>::other(optional)[2] |
B
|
|
| Expression | Return type | Requirements |
|---|---|---|
| *p | T&
|
|
| *cp | const T& | *cp and *p identify the same object. |
| p->m | (as is) | Same as (*p).m, if (*p).m |
| cp->m | (as is) | Same as (*cp).m, if (*cp).m |
| static_cast <A:: pointer > (vp) | (as is) | static_cast <A:: pointer > (vp) == |
| static_cast <A:: const_pointer > (cvp) | (as is) | static_cast <A:: const_pointer > (cvp) == |
| std::pointer_traits <A:: pointer > :: pointer_to (r) | (as is) |
| Expression | Return type | Requirements |
|---|---|---|
| a.allocate(n) | A::pointer
|
Allocates storage suitable for an array object of type T[n] and creates the array, but does not construct array elements. May throw exceptions. If n == 0
|
| a.allocate(n, cvp) (optional) | Same as a.allocate(n), but may use cvp (nullptr or a pointer obtained from a.allocate() | |
| a.allocate_at_least(n) (optional) (since C++23) |
std::allocation_result
<A::pointer> |
Allocates storage suitable for an array object of type T[cnt] and creates the array, but does not construct array elements, then returns {p, cnt}, where p points to the storage and cnt is not less than n
|
| a.deallocate(p, n) | (not used) | Deallocates storage pointed to p, which must be a value returned by a previous call to allocate
or allocate_at_least(since C++23) that has not been invalidated by an intervening call to deallocate. n must match the value previously passed to allocate
or be between the request and returned number of elements via allocate_at_least (may be equal to either bound)
(since C++23)
|
| a.max_size() (optional) | A::size_type
|
The largest value that can be passed to A::allocate(). |
| a.construct(xp, args...) (optional) | (not used) | Constructs an object of type X in previously-allocated storage at the address pointed to by xp, using args...
|
| a.destroy(xp) (optional) | (not used) | Destructs an object of type X pointed to by xp, but does not deallocate any storage.
|
| Expression | Return type | Requirements |
|---|---|---|
| a1 == a2 | bool |
|
| a1 != a2 |
|
|
| Declaration | Effect | Requirements |
| A a1(a) | Copy-constructs a1 such that a1 == a. (Note: Every Allocator also satisfies CopyConstructible |
|
| A a1 = a | ||
| A a(b) | Constructs a such that B(a) == b and A(b) == a. (Note: This implies that all allocators related by rebind
|
|
| A a1(std::move(a)) | Constructs a1 such that it equals the prior value of a. |
|
| A a1 = std::move(a) | ||
| A a(std::move(b)) | Constructs a such that it equals the prior value of A(b) |
|
| Type-id | Aliased type | Requirements |
A::is_always_equal(optional) |
std::true_type or std::false_type |
|
| Expression | Return type | Description |
|---|---|---|
| a.select_on_container_copy_construction() (optional) |
A
|
|
| Type-id | Aliased type | Description |
A::propagate_on_container_copy_assignment(optional) |
std::true_type or std::false_type |
|
A::propagate_on_container_move_assignment(optional) |
|
|
A::propagate_on_container_swap(optional) |
|
Notes:
- ↑ See also fancy pointers below.
-
↑
rebindis only optional (provided by std::allocator_traits) if this allocator is a template of the formSomeAllocator<T, Args>, whereArgs
Given
-
x1 and x2, objects of (possibly different) types
X::void_pointer,X::const_void_pointer,X::pointer, orX::const_pointer
- Then, x1 and x2 are equivalently-valued pointer values, if and only if both x1 and x2 can be explicitly converted to the two corresponding objects px1 and px2 of type
X::const_pointer, using a sequence of static_casts using only these four types, and the expression px1 == px2 evaluates to true
Given
- w1 and w2, objects of type
X::void_pointer
- Then, for the expression w1 == w2 and w1 != w2 either or both objects may be replaced by an equivalently-valued object of type
X::const_void_pointer
Given
- p1 and p2, objects of type
X::pointer
- Then, for the expressions p1 == p2, p1 != p2, p1 < p2, p1 <= p2, p1 >= p2, p1 > p2, p1 - p2 either or both objects may be replaced by an equivalently-valued object of type
X::const_pointer
The above requirements make it possible to compare Container's iterators and const_iterator
Allocator completeness requirementsAn allocator type
|
(since C++17) |
Stateful and stateless allocators
Every Allocator type is either stateful or stateless
|
Although custom allocators are not required to be stateless, whether and how the use of stateful allocators in the standard library is implementation-defined. Use of unequal allocator values may result in implementation-defined runtime errors or undefined behavior if the implementation does not support such usage. |
(until C++11) |
|
Custom allocators may contain state. Each container or another allocator-aware object stores an instance of the supplied allocator and controls allocator replacement through std::allocator_traits |
(since C++11) |
Instances of a stateless allocator type always compare equal. Stateless allocator types are typically implemented as empty classes and suitable for empty base class optimization.
|
The member type |
(since C++11) |
Fancy pointers
When the member type pointer is not a raw pointer type, it is commonly referred to as a "fancy pointer"
boost::interprocess::offset_ptr, which makes it possible to allocate node-based data structures such as
std::set
in shared memory and memory mapped files mapped in different addresses in every process. Fancy pointers can be used independently of the allocator that provided them
, through the class template std::pointer_traits
(since C++11)
. The function std::to_address can be used to obtain a raw pointer from a fancy pointer.(since C++20)
|
Use of fancy pointers and customized size/different type in the standard libary are conditionally supported. Implementations may require that member type |
(until C++11) |
ConceptFor the definition of the query object std::get_allocator, the following exposition-only concept is defined.
The exposition-only concept /*simple-allocator*/ defines the minimal usability constraints of the Allocator |
(since C++26) |
Standard library
The following standard library components satisfy the Allocator requirements:
| the default allocator (class template) |
|
|
(C++11)
|
implements multi-level allocator for multi-level containers (class template) |
|
(C++17)
|
an allocator that supports run-time polymorphism based on the std::pmr::memory_resource it is constructed with (class template) |
Examples
Demonstrates a C++11 allocator, except for [[nodiscard]] added to match C++20 style.
#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); }
Possible output:
Alloc: 32 bytes at 0x2020c20 Alloc: 64 bytes at 0x2023c60 Dealloc: 32 bytes at 0x2020c20 Dealloc: 64 bytes at 0x2023c60
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| LWG 179 | C++98 | pointer and const_pointer were notrequired to be comparable with each other |
required |
| LWG 199 | C++98 | the return value of a.allocate(0) was unclear | it is unspecified |
| LWG 258 (N2436) |
C++98 | the equality relationship between allocators were not required to be reflexive, symmetric or transitive |
required to be reflexive, symmetric and transitive |
| LWG 274 | C++98 |
T could be a const-qualified type or reference type,making std::allocator possibly ill-formed[1] |
prohibited these types |
| LWG 2016 | C++11 | the copy, move and swap operations of allocator might be throwing when used |
required to be non-throwing |
| LWG 2081 | C++98 C++11 |
allocators were not required to support copy assignment (C++98) and move assignment (C++11) |
required |
| LWG 2108 | C++11 | there was no way to show an allocator is stateless | is_always_equal provided
|
| LWG 2263 | C++11 | the resolution of LWG issue 179 was accidently dropped in C++11 and not generalized to void_pointer and const_void_pointer
|
restored and generalized |
| LWG 2447 | C++11 | T could be a volatile-qualified object type
|
prohibited these types |
| LWG 2593 | C++11 | moving from an allocator might modify its value | modification forbidden |
| P0593R6 | C++98 | allocate were not required to create anarray object in the storage it allocated |
required |
-
↑
The member types
referenceandconst_referenceof std::allocator are defined asT&andconst T&respectively.- If
Tis a reference type,referenceandconst_referenceare ill-formed because reference to reference cannot be formed (reference collapsing - If
Tis const-qualified,referenceandconst_referenceare the same, and the overload set of address()
- If