Difference between revisions of "cpp/named req/Allocator"
(Example: applied that Mallocator to std::vector) |
(→Requirements: LWG2016, LWG2593) |
||
Line 173: | Line 173: | ||
|rowspan=2| | |rowspan=2| | ||
* Does not throw exceptions. | * Does not throw exceptions. | ||
− | * | + | * The value of {{tt|a}} is unchanged and {{c|a1 {{==}} a}}. |
|- | |- | ||
| {{c|A a1 {{=}} std::move(a)}} | | {{c|A a1 {{=}} std::move(a)}} | ||
Line 207: | Line 207: | ||
|rowspan=3| {{lc|std::true_type}} or {{lc|std::false_type}} or derived from such. | |rowspan=3| {{lc|std::true_type}} or {{lc|std::false_type}} or derived from such. | ||
| | | | ||
− | * {{ | + | * {{lc|std::true_type}} or derived from it if the allocator of type {{tt|A}} needs to be copied when the container that uses it is copy-assigned. |
+ | * If this member is {{lc|std::true_type}} or derived from it, then {{tt|A}} must satisfy {{named req|CopyAssignable}} and the copy operation must not throw exceptions. | ||
* Note that if the allocators of the source and the target containers do not compare equal, copy assignment has to deallocate the target's memory using the old allocator and then allocate it using the new allocator before copying the elements (and the allocator). | * Note that if the allocators of the source and the target containers do not compare equal, copy assignment has to deallocate the target's memory using the old allocator and then allocate it using the new allocator before copying the elements (and the allocator). | ||
|- | |- | ||
| {{c|A::propagate_on_container_move_assignment}} <br/> {{mark optional}} | | {{c|A::propagate_on_container_move_assignment}} <br/> {{mark optional}} | ||
| | | | ||
− | * {{ | + | * {{lc|std::true_type}} or derived from it if the allocator of type {{tt|A}} needs to be moved when the container that uses it is move-assigned. |
− | * If this member is {{ | + | * If this member is {{lc|std::true_type}} or derived from it, then {{tt|A}} must satisfy {{named req|MoveAssignable}} and the move operation must not throw exceptions. |
+ | * If this member is not provided or derived from {{lc|std::false_type}} and the allocators of the source and the target containers do not compare equal, move assignment cannot take ownership of the source memory and must move-assign or move-construct the elements individually, resizing its own memory as needed. | ||
|- | |- | ||
| {{c|A::propagate_on_container_swap}} <br/> {{mark optional}} | | {{c|A::propagate_on_container_swap}} <br/> {{mark optional}} | ||
| | | | ||
− | * {{ | + | * {{lc|std::true_type}} or derived from it if the allocators of type {{tt|A}} need to be swapped when two containers that use them are swapped. |
− | * If this member is {{ | + | * If this member is {{lc|std::true_type}} or derived from it, lvalues of {{tt|A}} must be {{named req|Swappable}} and the swap operation must not throw exceptions. |
+ | * If this member is not provided or derived from {{lc|std::false_type}} and the allocators of the two containers do not compare equal, the behavior of container swap is undefined. | ||
|} | |} | ||
Line 242: | Line 245: | ||
The above requirements make it possible to compare {{named req|Container}}'s iterators and const_iterators. | The above requirements make it possible to compare {{named req|Container}}'s iterators and const_iterators. | ||
}} | }} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
{{rrev | since=c++17 | | {{rrev | since=c++17 | |
Revision as of 18:00, 2 August 2020
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
, to std::shared_ptr
and std::function
, 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 allocator-aware classes, including standard library containers, access allocators indirectly through std::allocator_traits
, and std::allocator_traits supplies the default implementation of those requirements.
Contents |
Requirements
Given
-
T
, a cv-unqualified object type -
A
, an Allocator type for typeT
-
a
, an object of typeA
-
B
, the corresponding Allocator type for some cv-unqualified object typeU
(as obtained by rebindingA
) -
b
, an object of typeB
-
p
, a value of typeallocator_traits<A>::pointer
, obtained by calling allocator_traits<A>::allocate() -
cp
, a value of typeallocator_traits<A>::const_pointer
, obtained by conversion fromp
-
vp
, a value of typeallocator_traits<A>::void_pointer
, obtained by conversion fromp
-
cvp
, a value of typeallocator_traits<A>::const_void_pointer
, obtained by conversion fromcp
or fromvp
-
xp
, a dereferencable pointer to some cv-unqualified object typeX
-
r
, an lvalue of typeT
obtained by the expression *p -
n
, a value of typeallocator_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. |
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.deallocate(p, n) | (not used) | Deallocates storage pointed to p , which must be a value returned by a previous call to allocate that has not been invalidated by an intervening call to deallocate . n must match the value previously passed to allocate . 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) (since C++17) |
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 form SomeAllocator<T, Args>, whereArgs
is zero or more additional template type parameters.
Given
Then, x1 and x2 are equivalently-valued pointer values, if and only if both Given
Then for the expression w1 == w2 and w1 != w2 either or both objects may be replaced by an equivalently-valued object of type Given
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 The above requirements make it possible to compare Container's iterators and const_iterators. |
(since C++14) |
Allocator completeness requirementsAn allocator type
|
(since C++17) |
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)
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
A C++11 allocator, except for [[nodiscard]] added to match C++20 style
#include <cstdlib> #include <new> #include <limits> #include <iostream> #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_alloc(); 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