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 : an object of a class type that satisfies the following requirements.
The implementation of many allocator requirements is optional because all AllocatorAwareContainer access allocators indirectly through std::allocator_traits , and std::allocator_traits supplies the default implementation of those requirements.
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
T
obtained 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 is well-defined. |
cp - > m | (as is) | Same as ( * cp ) . m , if ( * cp ) . m is well-defined. |
static_cast < A :: pointer > ( vp ) | (as is) | static_cast < A :: pointer > ( vp ) == p |
static_cast < A :: const_pointer > ( cvp ) | (as is) | static_cast < A :: const_pointer > ( cvp ) == cp |
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
, the return value is unspecified.
|
a. allocate ( n, cvp ) (optional) | Same as a. allocate ( n ) , but may use cvp ( nullptr or a pointer obtained from a. allocate ( ) ) in unspecified manner to aid locality. | |
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
. May throw exceptions.
|
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)
. Does not throw exceptions.
|
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...
as the constructor arguments.
|
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
maintain each other's resources, such as memory pools.)
|
|
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 or derived from such. |
|
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 or derived from such. |
|
A::propagate_on_container_move_assignment
(optional) |
|
|
A::propagate_on_container_swap
(optional) |
|
Notes:
- ↑ See also fancy pointers below.
-
↑
rebind
is only optional (provided by std::allocator_traits ) if this allocator is a template of the formSomeAllocator<T, Args>
, whereArgs
is zero or more additional template type parameters.
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_cast s 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
with no change in semantics.
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
with no change in semantics.
The above requirements make it possible to compare
Container
's
iterator
s and
const_iterator
s.
Allocator completeness requirements
An allocator type
|
(since C++17) |
Stateful and stateless allocators
Every Allocator type is either stateful or stateless . Generally, a stateful allocator type can have unequal values which denote distinct memory resources, while a stateless allocator type denotes a single memory resource.
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"
. Such pointers were introduced to support segmented memory architectures and are used today to access objects allocated in address spaces that differ from the homogeneous virtual address space that is accessed by raw pointers. An example of a fancy pointer is the mapping address-independent 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 requirement. |
(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 not
required 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 an
array object in the storage it allocated |
required |
-
↑
The member types
reference
andconst_reference
of std::allocator are defined asT&
andconst T&
respectively.-
If
T
is a reference type,reference
andconst_reference
are ill-formed because reference to reference cannot be formed ( reference collapsing was introduced in C++11). -
If
T
is const-qualified,reference
andconst_reference
are the same, and the overload set of address() is ill-formed.
-
If