Difference between revisions of "cpp/named req/Allocator"
m (fmt) |
YexuanXiao (Talk | contribs) m (P2696R0) |
||
(31 intermediate revisions by 11 users not shown) | |||
Line 4: | Line 4: | ||
Encapsulates strategies for access/addressing, allocation/deallocation and construction/destruction of objects. | 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 {{lc|std::string}}, {{lc|std::vector}}, and every container except {{lc|std::array}}, to {{lc|std::shared_ptr}} and {{lc|std::function}}, does so through an {{named req|Allocator}}: an object of a class type that satisfies the following requirements. | + | Every standard library component that may need to allocate or release storage, from {{lc|std::string}}, {{lc|std::vector}}, and every container{{rev inl|since=c++11|, except {{lc|std::array}}}}{{rev inl|since=c++26| and {{lc|std::inplace_vector}}}}, to {{lc|std::shared_ptr}}{{rev inl|until=c++17| and {{lc|std::function}}}}, does so through an {{named req|Allocator}}: an object of a class type that satisfies the following requirements. |
− | The implementation of many allocator requirements is optional because all | + | The implementation of many allocator requirements is optional because all {{named req|AllocatorAwareContainer}} access allocators indirectly through {{lc|std::allocator_traits}}, and {{lc|std::allocator_traits}} supplies the default implementation of those requirements. |
===Requirements=== | ===Requirements=== | ||
− | |||
Given | Given | ||
− | * {{tt|T}}, a cv-unqualified object type | + | * {{tt|T}}, a {{rev inl|until=c++11|non-const, non-reference type}}{{rev inl|since=c++11|until=c++17|non-const object type}}{{rev inl|since=c++17|cv-unqualified object type}}, |
− | * {{tt|A}}, an {{named req/core|Allocator}} type for type {{tt|T}} | + | * {{tt|A}}, an {{named req/core|Allocator}} type for type {{tt|T}}, |
− | * {{ | + | * {{c|a}}, an object of type {{tt|A}}, |
− | * {{tt|B}}, the corresponding {{named req/core|Allocator}} type for some cv-unqualified object type {{tt|U}} (as obtained by rebinding {{tt|A}}) | + | * {{tt|B}}, the corresponding {{named req/core|Allocator}} type for some cv-unqualified object type {{tt|U}} (as obtained by rebinding {{tt|A}}), |
− | * {{ | + | * {{c|b}}, an object of type {{tt|B}}, |
− | * {{ | + | * {{c|p}}, a value of type {{c/core|std::allocator_traits<A>::pointer}}, obtained by calling {{c|std::allocator_traits<A>::allocate()}}, |
− | * {{ | + | * {{c|cp}}, a value of type {{c/core|std::allocator_traits<A>::const_pointer}}, obtained by conversion from {{c|p}}, |
− | * {{ | + | * {{c|vp}}, a value of type {{c/core|std::allocator_traits<A>::void_pointer}}, obtained by conversion from {{c|p}}, |
− | * {{ | + | * {{c|cvp}}, a value of type {{c/core|std::allocator_traits<A>::const_void_pointer}}, obtained by conversion from {{c|cp}} or from {{c|vp}}, |
− | * {{ | + | * {{c|xp}}, a dereferenceable pointer to some cv-unqualified object type {{tt|X}}, |
− | * {{ | + | * {{c|r}}, an lvalue of type {{tt|T}} obtained by the expression {{c|*p}}, |
− | * {{ | + | * {{c|n}}, a value of type {{c/core|std::allocator_traits<A>::size_type}}. |
{|table class=wikitable | {|table class=wikitable | ||
Line 29: | Line 28: | ||
!Type-id||Aliased type||Requirements | !Type-id||Aliased type||Requirements | ||
|- | |- | ||
− | | {{ | + | |{{tt|A::pointer}} {{mark optional}} |
− | | ''(unspecified)''<ref>See also [[# | + | |''(unspecified)''<ref>See also [[#Fancy pointers|fancy pointers]] below.</ref> |
| | | | ||
* Satisfies {{named req|NullablePointer}}, {{named req|RandomAccessIterator}}, and {{named req|ContiguousIterator}}. {{mark unreviewed dr|LWG|2794}} | * Satisfies {{named req|NullablePointer}}, {{named req|RandomAccessIterator}}, and {{named req|ContiguousIterator}}. {{mark unreviewed dr|LWG|2794}} | ||
|- | |- | ||
− | | {{ | + | |{{tt|A::const_pointer}} {{mark optional}} |
− | | ''(unspecified)'' | + | |''(unspecified)'' |
| | | | ||
* Satisfies {{named req|NullablePointer}}, {{named req|RandomAccessIterator}}, and {{named req|ContiguousIterator}}. | * Satisfies {{named req|NullablePointer}}, {{named req|RandomAccessIterator}}, and {{named req|ContiguousIterator}}. | ||
− | * {{ | + | * {{tt|A::pointer}} is convertible to {{tt|A::const_pointer}}. |
|- | |- | ||
− | | {{ | + | |{{tt|A::void_pointer}} {{mark optional}} |
− | | ''(unspecified)'' | + | |''(unspecified)'' |
| | | | ||
* Satisfies {{named req|NullablePointer}}. | * Satisfies {{named req|NullablePointer}}. | ||
− | * {{ | + | * {{tt|A::pointer}} is convertible to {{tt|A::void_pointer}}. |
− | * {{ | + | * {{tt|B::void_pointer}} and {{tt|A::void_pointer}} are the same type. |
|- | |- | ||
− | | {{ | + | |{{tt|A::const_void_pointer}} {{mark optional}} |
− | | ''(unspecified)'' | + | |''(unspecified)'' |
| | | | ||
* Satisfies {{named req|NullablePointer}}. | * Satisfies {{named req|NullablePointer}}. | ||
− | * {{ | + | * {{tt|A::pointer}}, {{tt|A::const_pointer}}, and {{tt|A::void_pointer}} are convertible to {{tt|A::const_void_pointer}}. |
− | * {{ | + | * {{tt|B::const_void_pointer}} and {{tt|A::const_void_pointer}} are the same type. |
|- | |- | ||
− | | {{ | + | |{{tt|A::value_type}} |
− | | {{tt|T}} | + | |{{tt|T}} |
− | | | + | | |
|- | |- | ||
− | | {{ | + | |{{tt|A::size_type}} {{mark optional}} |
− | | ''(unspecified)'' | + | |''(unspecified)'' |
| | | | ||
* An unsigned integer type. | * An unsigned integer type. | ||
* Can represent the size of the largest object {{tt|A}} can allocate. | * Can represent the size of the largest object {{tt|A}} can allocate. | ||
|- | |- | ||
− | | {{ | + | |{{tt|A::difference_type}} {{mark optional}} |
− | | ''(unspecified)'' | + | |''(unspecified)'' |
| | | | ||
* A signed integer type. | * A signed integer type. | ||
* Can represent the difference of any two pointers to the objects allocated by {{tt|A}}. | * Can represent the difference of any two pointers to the objects allocated by {{tt|A}}. | ||
|- | |- | ||
− | | {{ | + | |{{tt|A::template rebind<U>::other}}<br>{{mark optional}}<ref>{{tt|rebind}} is only optional (provided by {{lc|std::allocator_traits}}) if this allocator is a template of the form {{tt|SomeAllocator<T, Args>}}, where {{tt|Args}} is zero or more additional template type parameters.</ref> |
− | | {{tt|B}} | + | |{{tt|B}} |
| | | | ||
− | * For any {{tt|U}}, {{ | + | * For any {{tt|U}}, {{tt|B::template rebind<T>::other}} is {{tt|A}}. |
|} | |} | ||
Line 81: | Line 80: | ||
!Expression||Return type||Requirements | !Expression||Return type||Requirements | ||
|- | |- | ||
− | | {{c|*p}} | + | |{{c|*p}} |
− | | {{ | + | |{{tt|T&}} |
− | | | + | | |
|- | |- | ||
− | | {{c|*cp}} | + | |{{c|*cp}} |
− | | {{c|const T&}} | + | |{{c/core|const T&}} |
− | | {{c|*cp}} and {{c|*p}} identify the same object. | + | |{{c|*cp}} and {{c|*p}} identify the same object. |
|- | |- | ||
− | | {{c|p->m}} | + | |{{c|p->m}} |
− | | <abbr title="The type of T::m.">''(as is)''</abbr> | + | |<abbr title="The type of T::m.">''(as is)''</abbr> |
− | | Same as {{c|(*p).m}}, if {{c|(*p).m}} is well-defined. | + | |Same as {{c|(*p).m}}, if {{c|(*p).m}} is well-defined. |
|- | |- | ||
− | | {{c|cp->m}} | + | |{{c|cp->m}} |
− | | <abbr title="The type of T::m.">''(as is)''</abbr> | + | |<abbr title="The type of T::m.">''(as is)''</abbr> |
− | | Same as {{c|(*cp).m}}, if {{c|(*cp).m}} is well-defined. | + | |Same as {{c|(*cp).m}}, if {{c|(*cp).m}} is well-defined. |
|- | |- | ||
− | | {{c|static_cast<A::pointer>(vp)}} | + | |{{c|static_cast<A::pointer>(vp)}} |
− | | <abbr title="A::pointer">''(as is)''</abbr> | + | |<abbr title="A::pointer">''(as is)''</abbr> |
− | | {{c|1=static_cast<A::pointer>(vp) == p}} | + | |{{c|1=static_cast<A::pointer>(vp) == p}} |
|- | |- | ||
− | | {{c|static_cast<A::const_pointer>(cvp)}} | + | |{{c|static_cast<A::const_pointer>(cvp)}} |
− | | <abbr title="A::const_pointer">''(as is)''</abbr> | + | |<abbr title="A::const_pointer">''(as is)''</abbr> |
− | | {{c|1=static_cast<A::const_pointer>(cvp) == cp}} | + | |{{c|1=static_cast<A::const_pointer>(cvp) == cp}} |
|- | |- | ||
− | | {{c|std::pointer_traits<A::pointer>::pointer_to(r)}}{{mark unreviewed dr|LWG|2260}} | + | |{{c|std::pointer_traits<A::pointer>::pointer_to(r)}}{{mark unreviewed dr|LWG|2260}} |
− | | <abbr title="A::pointer">''(as is)''</abbr> | + | |<abbr title="A::pointer">''(as is)''</abbr> |
| | | | ||
|} | |} | ||
Line 115: | Line 114: | ||
!Expression||Return type||Requirements | !Expression||Return type||Requirements | ||
|- | |- | ||
− | | {{c|a.allocate(n)}} | + | |{{c|a.allocate(n)}} |
− | |rowspan=2| {{ | + | |rowspan=2|{{tt|A::pointer}} |
− | | Allocates storage suitable for an array object of type {{ | + | |Allocates storage suitable for an array object of type {{tt|T[n]}} and creates the array, but does not construct array elements. May throw exceptions. If {{c|1=n == 0}}, the return value is unspecified. |
|- | |- | ||
− | | {{c|a.allocate(n, cvp)}} {{mark optional}} | + | |{{c|a.allocate(n, cvp)}} {{mark optional}} |
− | | Same as {{c|a.allocate(n)}}, but may use {{ | + | |Same as {{c|a.allocate(n)}}, but may use {{c|cvp}} ({{c|nullptr}} or a pointer obtained from {{c|a.allocate()}}) in unspecified manner to aid locality. |
|- | |- | ||
− | | {{c|a.allocate_at_least(n)}} {{mark optional}} {{mark since c++23}} | + | |{{c|a.allocate_at_least(n)}} {{mark optional}} {{mark since c++23}} |
− | | {{c|std::allocation_result | + | |{{c/core|std::allocation_result |
− | A::pointer>}} | + | <A::pointer>}} |
− | | Allocates storage suitable for an array object of type {{ | + | |Allocates storage suitable for an array object of type {{tt|T[cnt]}} and creates the array, but does not construct array elements, then returns {{c|{p, cnt}<!---->}}, where {{c|p}} points to the storage and {{c|cnt}} is not less than {{c|n}}. May throw exceptions. |
|- | |- | ||
− | | {{c|a.deallocate(p, n)}} | + | |{{c|a.deallocate(p, n)}} |
− | | ''(not used)'' | + | |''(not used)'' |
− | | Deallocates storage pointed to {{ | + | |Deallocates storage pointed to {{c|p}}, which must be a value returned by a previous call to {{tt|allocate}} {{rev inl|since=c++23|or {{tt|allocate_at_least}}}} that has not been invalidated by an intervening call to {{tt|deallocate}}. {{c|n}} must match the value previously passed to {{tt|allocate}}{{rev inl|since=c++23|or be between the request and returned number of elements via {{tt|allocate_at_least}} (may be equal to either bound)}}. Does not throw exceptions. |
|- | |- | ||
− | | {{c|a.max_size()}} {{mark optional}} | + | |{{c|a.max_size()}} {{mark optional}} |
− | | {{ | + | |{{tt|A::size_type}} |
− | | The largest value that can be passed to {{c|A::allocate()}}. | + | |The largest value that can be passed to {{c|A::allocate()}}. |
|- | |- | ||
− | | {{c|a.construct(xp, args)}} {{mark optional}} | + | |{{c|a.construct(xp, args...)}} {{mark optional}} |
− | | ''(not used)'' | + | |''(not used)'' |
− | | Constructs an object of type {{tt|X}} in previously-allocated storage at the address pointed to by {{ | + | |Constructs an object of type {{tt|X}} in previously-allocated storage at the address pointed to by {{c|xp}}, using {{c|args...}} as the constructor arguments. |
|- | |- | ||
− | | {{c|a.destroy(xp)}} {{mark optional}} | + | |{{c|a.destroy(xp)}} {{mark optional}} |
− | | ''(not used)'' | + | |''(not used)'' |
− | | Destructs an object of type {{tt|X}} pointed to by {{ | + | |Destructs an object of type {{tt|X}} pointed to by {{c|xp}}, but does not deallocate any storage. |
|} | |} | ||
Line 149: | Line 148: | ||
!Expression||Return type||Requirements | !Expression||Return type||Requirements | ||
|- | |- | ||
− | | {{c|1=a1 == a2}} | + | |{{c|1=a1 == a2}} |
− | |rowspan=2| {{c|bool}} | + | |rowspan=2|{{c/core|bool}} |
| | | | ||
− | * {{c|true}} only if the storage allocated by the allocator {{ | + | * {{c|true}} only if the storage allocated by the allocator {{c|a1}} can be deallocated through {{c|a2}}. |
* Establishes reflexive, symmetric, and transitive relationship. | * Establishes reflexive, symmetric, and transitive relationship. | ||
* Does not throw exceptions. | * Does not throw exceptions. | ||
|- | |- | ||
− | | {{c|1=a1 != a2}} | + | |{{c|1=a1 != a2}} |
| | | | ||
− | * Same as {{c|1=!(a1==a2)}}. | + | * Same as {{c|1=!(a1 == a2)}}. |
|- | |- | ||
!Declaration||Effect||Requirements | !Declaration||Effect||Requirements | ||
|- | |- | ||
− | | {{c|A a1(a)}} | + | |{{c|A a1(a)}} |
− | |rowspan=2| Copy-constructs {{ | + | |rowspan=2|Copy-constructs {{c|a1}} such that {{c|1=a1 == a}}.<br>(Note: Every {{named req/core|Allocator}} also satisfies {{named req|CopyConstructible}}.) |
|rowspan=2| | |rowspan=2| | ||
* Does not throw exceptions. | * Does not throw exceptions. | ||
|- | |- | ||
− | | {{c|A a1 | + | |{{c|1=A a1 = a}} |
|- | |- | ||
− | | {{c|A a(b)}} | + | |{{c|A a(b)}} |
− | | Constructs {{ | + | |Constructs {{c|a}} such that {{c|1=B(a) == b}} and {{c|1=A(b) == a}}.<br>(Note: This implies that all allocators related by {{tt|rebind}} maintain each other's resources, such as memory pools.) |
| | | | ||
* Does not throw exceptions. | * Does not throw exceptions. | ||
|- | |- | ||
− | | {{c|A a1(std::move(a))}} | + | |{{c|A a1(std::move(a))}} |
− | |rowspan=2| Constructs {{ | + | |rowspan=2|Constructs {{c|a1}} such that it equals the prior value of {{c|a}}. |
|rowspan=2| | |rowspan=2| | ||
* Does not throw exceptions. | * Does not throw exceptions. | ||
− | * The value of {{ | + | * The value of {{c|a}} is unchanged and {{c|1=a1 == a}}. |
|- | |- | ||
− | | {{c|A a1 | + | |{{c|1=A a1 = std::move(a)}} |
|- | |- | ||
− | | {{c|A a(std::move(b))}} | + | |{{c|A a(std::move(b))}} |
− | | Constructs {{ | + | |Constructs {{c|a}} such that it equals the prior value of {{c|A(b)}}. |
| | | | ||
* Does not throw exceptions. | * Does not throw exceptions. | ||
Line 189: | Line 188: | ||
!Type-id||Aliased type||Requirements | !Type-id||Aliased type||Requirements | ||
|- | |- | ||
− | | {{ | + | |{{tt|A::is_always_equal}}<br>{{mark optional}}<!--LWG2108--> |
− | | {{lc|std::true_type}} or {{lc|std::false_type}} or derived from such. | + | |{{lc|std::true_type}} or {{lc|std::false_type}} or derived from such. |
| | | | ||
* {{c|true}} if any two allocators of type {{tt|A}} always compare equal. | * {{c|true}} if any two allocators of type {{tt|A}} always compare equal. | ||
− | * (If not provided, {{lc|std::allocator_traits}} defaults this to {{c|std::is_empty<A>::type}}.) | + | * (If not provided, {{lc|std::allocator_traits}} defaults this to {{c/core|std::is_empty<A>::type}}.) |
|} | |} | ||
Line 201: | Line 200: | ||
!Expression||Return type||Description | !Expression||Return type||Description | ||
|- | |- | ||
− | | {{c|a.select_on_container_copy_construction()}} <br | + | |{{c|a.select_on_container_copy_construction()}}<br>{{mark optional}} |
− | | {{tt|A}} | + | |{{tt|A}} |
| | | | ||
− | * Provides an instance of {{tt|A}} to be used by the container that is copy-constructed from the one that uses {{ | + | * Provides an instance of {{tt|A}} to be used by the container that is copy-constructed from the one that uses {{c|a}} currently. |
− | * (Usually returns either a copy of {{ | + | * (Usually returns either a copy of {{c|a}} or a default-constructed {{tt|A}}.) |
|- | |- | ||
!Type-id||Aliased type||Description | !Type-id||Aliased type||Description | ||
|- | |- | ||
− | | {{ | + | |{{tt|A::propagate_on_container_copy_assignment}}<br>{{mark optional}} |
− | |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. | * {{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. | ||
Line 216: | Line 215: | ||
* 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). | ||
|- | |- | ||
− | | {{ | + | |{{tt|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. | * {{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. | ||
Line 222: | Line 221: | ||
* 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. | * 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. | ||
|- | |- | ||
− | | {{ | + | |{{tt|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. | * {{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 {{lc|std::true_type}} or derived from it, | + | * If this member is {{lc|std::true_type}} or derived from it, type {{tt|A}} must satisfy {{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. | * 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. | ||
|} | |} | ||
Notes: | Notes: | ||
− | <references /> | + | <references/> |
Given | Given | ||
− | * {{ | + | * {{c|x1}} and {{c|x2}}, objects of (possibly different) types {{tt|X::void_pointer}}, {{tt|X::const_void_pointer}}, {{tt|X::pointer}}, or {{tt|X::const_pointer}} |
− | Then, x1 and x2 are ''equivalently-valued'' pointer values, if and only if both {{ | + | :Then, {{c|x1}} and {{c|x2}} are ''equivalently-valued'' pointer values, if and only if both {{c|x1}} and {{c|x2}} can be explicitly converted to the two corresponding objects {{c|px1}} and {{c|px2}} of type {{tt|X::const_pointer}}, using a sequence of {{c/core|static_cast}}s using only these four types, and the expression {{c|1=px1 == px2}} evaluates to {{c|true}}. |
Given | Given | ||
− | * {{ | + | * {{c|w1}} and {{c|w2}}, objects of type {{tt|X::void_pointer}} |
− | Then for the expression {{c|w1 | + | :Then, for the expression {{c|1=w1 == w2}} and {{c|1=w1 != w2}} either or both objects may be replaced by an ''equivalently-valued'' object of type {{tt|X::const_void_pointer}} with no change in semantics. |
Given | Given | ||
− | * {{ | + | * {{c|p1}} and {{c|p2}}, objects of type {{tt|X::pointer}} |
− | Then, for the expressions {{c|p1 | + | :Then, for the expressions {{c|1=p1 == p2}}, {{c|1=p1 != p2}}, {{c|p1 < p2}}, {{c|1=p1 <= p2}}, {{c|1=p1 >= p2}}, {{c|p1 > p2}}, {{c|p1 - p2}} either or both objects may be replaced by an ''equivalently-valued'' object of type {{tt|X::const_pointer}} with no change in semantics. |
The above requirements make it possible to compare {{named req|Container}}'s {{tt|iterator}}s and {{tt|const_iterator}}s. | The above requirements make it possible to compare {{named req|Container}}'s {{tt|iterator}}s and {{tt|const_iterator}}s. | ||
− | {{rrev | since=c++17 | | + | {{rrev|since=c++17| |
====Allocator completeness requirements==== | ====Allocator completeness requirements==== | ||
An allocator type {{tt|X}} for type {{tt|T}} additionally satisfies the ''allocator completeness requirements'' if both of the following are true regardless of whether {{tt|T}} is a complete type: | An allocator type {{tt|X}} for type {{tt|T}} additionally satisfies the ''allocator completeness requirements'' if both of the following are true regardless of whether {{tt|T}} is a complete type: | ||
− | * {{tt|X}} is a complete type | + | * {{tt|X}} is a complete type. |
− | * Except for {{tt|value_type}}, all the member types of {{c|std::allocator_traits<X>}} are complete types. | + | * Except for {{tt|value_type}}, all the member types of {{c/core|std::allocator_traits<X>}} are complete types. |
}} | }} | ||
Line 267: | Line 266: | ||
Instances of a stateless allocator type always compare equal. Stateless allocator types are typically implemented as empty classes and suitable for [[cpp/language/ebo|empty base class optimization]]. | Instances of a stateless allocator type always compare equal. Stateless allocator types are typically implemented as empty classes and suitable for [[cpp/language/ebo|empty base class optimization]]. | ||
− | {{rrev|since=c++ | + | {{rrev|since=c++11<!-- N4258 & LWG2108 -->| |
The member type {{tt|is_always_equal}} of {{lc|std::allocator_traits}} is intendedly used for determining whether an allocator type is stateless. | The member type {{tt|is_always_equal}} of {{lc|std::allocator_traits}} is intendedly used for determining whether an allocator type is stateless. | ||
}} | }} | ||
===Fancy pointers=== | ===Fancy pointers=== | ||
− | When the member type {{tt|pointer}} is not a raw pointer type, it is commonly referred to as a | + | When the member type {{tt|pointer}} is not a raw pointer type, it is commonly referred to as a {{stddoc|P0773R0|"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 [https://www.boost.org/doc/libs/release/doc/html/interprocess/offset_ptr.html {{tt|boost::interprocess::offset_ptr}}], which makes it possible to allocate node-based data structures such as {{lc|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{{rev inl|since=c++11|, through the class template {{lc|std::pointer_traits}}}}. {{rev inl|since=c++20|The function {{lc|std::to_address}} can be used to obtain a raw pointer from a fancy pointer.}} |
{{rrev|until=c++11| | {{rrev|until=c++11| | ||
− | Use of fancy pointers and customized size/different type in the standard libary are conditionally supported. Implementations may require that member type {{tt|pointer}}, {{tt|const_pointer}}, {{tt|size_type}}, and {{tt|difference_type}} are {{ | + | Use of fancy pointers and customized size/different type in the standard libary are conditionally supported. Implementations may require that member type {{tt|pointer}}, {{tt|const_pointer}}, {{tt|size_type}}, and {{tt|difference_type}} are {{tt|value_type*}}, {{c/core|const value_type*}}, {{lc|std::size_t}}, and {{lc|std::ptrdiff_t}}, respectively. |
+ | }} | ||
+ | |||
+ | {{rrev|since=c++26|1= | ||
+ | ===Concept=== | ||
+ | For the definition of the query object {{lc|std::get_allocator}}, the following exposition-only concept is defined. | ||
+ | |||
+ | {{dcl begin}} | ||
+ | {{dcl|1= | ||
+ | template<class Alloc> | ||
+ | concept /*simple-allocator*/ = requires(Alloc alloc, std::size_t n) | ||
+ | { | ||
+ | { *alloc.allocate(n) } -> std::same_as<typename Alloc::value_type&>; | ||
+ | { alloc.deallocate(alloc.allocate(n), n) }; | ||
+ | } && std::copy_constructible<Alloc> | ||
+ | && std::equality_comparable<Alloc>; | ||
+ | }} | ||
+ | {{dcl end}} | ||
+ | |||
+ | The exposition-only concept {{c/core|/*simple-allocator*/}} defines the minimal usability constraints of the {{named req|Allocator}} requirement. | ||
}} | }} | ||
Line 281: | Line 299: | ||
The following standard library components satisfy the {{named req/core|Allocator}} requirements: | The following standard library components satisfy the {{named req/core|Allocator}} requirements: | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc | cpp/memory/dsc allocator}} | + | {{dsc inc|cpp/memory/dsc allocator}} |
− | {{dsc inc | cpp/memory/dsc scoped_allocator_adaptor}} | + | {{dsc inc|cpp/memory/dsc scoped_allocator_adaptor}} |
− | {{dsc inc | cpp/memory/dsc polymorphic_allocator}} | + | {{dsc inc|cpp/memory/dsc polymorphic_allocator}} |
{{dsc end}} | {{dsc end}} | ||
===Examples=== | ===Examples=== | ||
− | {{example| | + | {{example|Demonstrates a C++11 allocator, except for {{attr|nodiscard}} added to match C++20 style. |
|code= | |code= | ||
#include <cstdlib> | #include <cstdlib> | ||
+ | #include <iostream> | ||
+ | #include <limits> | ||
#include <new> | #include <new> | ||
− | |||
− | |||
#include <vector> | #include <vector> | ||
− | template <class T> | + | template<class T> |
struct Mallocator | struct Mallocator | ||
{ | { | ||
− | + | typedef T value_type; | |
− | + | ||
− | + | Mallocator() = default; | |
− | + | ||
− | + | template<class U> | |
− | + | constexpr Mallocator(const Mallocator <U>&) noexcept {} | |
− | if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) | + | |
− | + | [[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: | private: | ||
− | + | void report(T* p, std::size_t n, bool alloc = true) const | |
− | std::cout << (alloc ? "Alloc: " : "Dealloc: ") << sizeof(T)*n | + | { |
− | + | 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> | + | template<class T, class U> |
bool operator==(const Mallocator <T>&, const Mallocator <U>&) { return true; } | bool operator==(const Mallocator <T>&, const Mallocator <U>&) { return true; } | ||
− | template <class T, class U> | + | |
+ | template<class T, class U> | ||
bool operator!=(const Mallocator <T>&, const Mallocator <U>&) { return false; } | bool operator!=(const Mallocator <T>&, const Mallocator <U>&) { return false; } | ||
int main() | int main() | ||
{ | { | ||
− | + | std::vector<int, Mallocator<int>> v(8); | |
− | + | v.push_back(42); | |
} | } | ||
|p=true | |p=true | ||
Line 348: | Line 372: | ||
===Defect reports=== | ===Defect reports=== | ||
{{dr list begin}} | {{dr list begin}} | ||
− | {{dr list item|wg=lwg|dr=179|std=C++98|before={{tt|pointer}} and {{tt|const_pointer}} were not required to be comparable with each other|after=required}} | + | {{dr list item|wg=lwg|dr=179|std=C++98|before={{tt|pointer}} and {{tt|const_pointer}} were not<br>required to be comparable with each other|after=required}} |
− | {{dr list item| | + | {{dr list item|wg=lwg|dr=199|std=C++98|before=the return value of {{c|a.allocate(0)}} was unclear|after=it is unspecified}} |
− | {{dr list item|wg=lwg|dr=2016|std=C++11|before=the copy, move and swap operations of allocator might be throwing when used|after=required to be non-throwing}} | + | {{dr list item|wg=lwg|dr=258|paper=N2436|std=C++98|before=the equality relationship between allocators were<br>not required to be reflexive, symmetric or transitive|after=required to be reflexive,<br>symmetric and transitive}} |
− | {{dr list item|wg=lwg|dr=2263|std=C++11|before=resolution of | + | {{dr list item|wg=lwg|dr=274|std=C++98|before={{tt|T}} could be a const-qualified type or reference type,<br>making {{lc|std::allocator}} possibly ill-formed<ref>The member types {{tt|reference}} and {{tt|const_reference}} of {{lc|std::allocator}} are defined as {{tt|T&}} and {{tt|const T&}} respectively. |
+ | * If {{tt|T}} is a reference type, {{tt|reference}} and {{tt|const_reference}} are ill-formed because reference to reference cannot be formed ([[cpp/language/reference#Reference collapsing|reference collapsing]] was introduced in C++11). | ||
+ | * If {{tt|T}} is const-qualified, {{tt|reference}} and {{tt|const_reference}} are the same, and the overload set of {{ltf|cpp/memory/allocator/address}} is ill-formed.</ref>|after=prohibited these types}} | ||
+ | <!--although LWG 402 is resolved, the resolution targets C++98 and the defective requirement was removed in C++11--> | ||
+ | {{dr list item|wg=lwg|dr=2016|std=C++11|before=the copy, move and swap operations of<br>allocator might be throwing when used|after=required to be non-throwing}} | ||
+ | {{dr list item|wg=lwg|dr=2081|std=C++98<br>C++11|before=allocators were not required to support copy<br>assignment (C++98) and move assignment (C++11)|after=required}} | ||
+ | {{dr list item|wg=lwg|std=C++11|dr=2108|before=there was no way to show an allocator is stateless|after={{tt|is_always_equal}} provided}} | ||
+ | {{dr list item|wg=lwg|dr=2263|std=C++11|before=the resolution of {{lwg|179}} was accidently dropped in C++11<br>and not generalized to {{tt|void_pointer}} and {{tt|const_void_pointer}}|after=restored and generalized}} | ||
+ | {{dr list item|wg=lwg|dr=2447|std=C++11|before={{tt|T}} could be a volatile-qualified object type|after=prohibited these types}} | ||
{{dr list item|wg=lwg|dr=2593|std=C++11|before=moving from an allocator might modify its value|after=modification forbidden}} | {{dr list item|wg=lwg|dr=2593|std=C++11|before=moving from an allocator might modify its value|after=modification forbidden}} | ||
+ | {{dr list item|paper=P0593R6|std=C++98|before={{tt|allocate}} were not required to create an<br>array object in the storage it allocated|after=required}} | ||
{{dr list end}} | {{dr list end}} | ||
+ | <references/> | ||
{{langlinks|de|es|fr|it|ja|pt|ru|zh}} | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Latest revision as of 14:05, 20 October 2024
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.
Contents |
[edit] 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_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
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 requirementsAn allocator type
|
(since C++17) |
[edit] 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) |
[edit] 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) |
[edit] 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) |
[edit] 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
[edit] 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
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