Section: 16.4.4.6.1 [allocator.requirements.general] Status: C++23 Submitter: Alisdair Meredith Opened: 2022-09-22 Last modified: 2023-11-22
Priority: Not Prioritized
View other active issues in [allocator.requirements.general].
View all other issues in [allocator.requirements.general].
View all issues with C++23 status.
Discussion:
This issue is extracted from P0177R2 as that paper stalled on the author's ability to update in time for C++17. While the issue was recorded and going to be resolved in the paper, we did not file an issue for the list when work on that paper stopped.
Many of the types and expressions in the Cpp17Allocator requirements are optional, and as such a default is provided that is exposed throughstd::allocator_traits
. However, some types and operations are
specified directly in terms of the allocator member, when really they should be
specified allowing for reliance on the default, obtained through
std::allocator_traits
. For example, X::pointer
is
an optional type and not required to exist; XX::pointer
is either
X::pointer
when it is present, or the default formula otherwise,
and so is guaranteed to always exist, and the intended interface for user code.
Observe that bullet list in p2, which acts as the key to the names in the
Cpp17Allocator requirements, gets this right, unlike most of the text
that follows.
This change corresponds to the known implementations, which meet the intended
contract rather than that currently specified. For example,
std::allocator
does not provide any of the pointer
related typedef members, so many of the default semantics indicated today would
be ill-formed if implementations were not already implementing the fix.
An alternative resolution might be to add wording around p1-3 to state that if
a name lookup fails then the default formula is used. However, it is simply
clearer to write the constraints as intended, in the form of code that users
can write, rather than hide behind a layer of indirect semantics that may be
interpreted as requiring another layer of SFINAE metaprogramming.
[2022-10-12; Reflector poll]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4917.
Modify 16.4.4.6.1 [allocator.requirements.general] as indicated:
typename X::pointer-4- Remarks: Default:
T*
typename X::const_pointer-5- Mandates:
-6- Remarks: Default:XX::pointer
is convertible toXX::const_pointer
.pointer_traits<XX::pointer>::rebind<const T>
typename X::void_pointer typename Y::void_pointer-7- Mandates:
-8- Remarks: Default:XX::pointer
is convertible toXX::void_pointer
.XX::void_pointer
andYY::void_pointer
are the same type.pointer_traits<XX::pointer>::rebind<void>
typename X::const_void_pointer typename Y::const_void_pointer-9- Mandates:
-10- Remarks: Default:XX::pointer
,XX::const_pointer
, andXX::void_pointer
are convertible toXX::const_void_pointer
.XX::const_void_pointer
andYY::const_void_pointer
are the same type.pointer_traits<XX::pointer>::rebind<const void>
typename X::value_type-11- Result: Identical to
T
.typename X::size_type-12- Result: An unsigned integer type that can represent the size of the largest object in the allocation model.
-13- Remarks: Default:make_unsigned_t<XX::difference_type>
typename X::difference_type-14- Result: A signed integer type that can represent the difference between any two pointers in the allocation model.
-15- Remarks: Default:pointer_traits<XX::pointer>::difference_type
typename X::template rebind<U>::other-16- Result:
-17- Postconditions: For allY
U
(includingT
),YY::
istemplate rebindrebind_alloc<T>::otherX
. -18- Remarks: IfAllocator
is a class template instantiation of the formSomeAllocator<T, Args>
, whereArgs
is zero or more type arguments, andAllocator
does not supply arebind
member template, the standardallocator_traits
template usesSomeAllocator<U, Args>
in place ofAllocator::rebind<U>::other
by default. For allocator types that are not template instantiations of the above form, no default is provided. -19- [Note 1: The member class templaterebind
ofX
is effectively a typedef template. In general, if the nameAllocator
is bound toSomeAllocator<T>
, thenAllocator::rebind<U>::other
is the same type asSomeAllocator<U>
, whereSomeAllocator<T>::value_type
isT
andSomeAllocator<U>::value_type
isU
. — end note][…]
static_cast<XX::pointer>(w)-29- Result:
-30- Postconditions:XX::pointer
static_cast<XX::pointer>(w) == p
.static_cast<XX::const_pointer>(x)-31- Result:
-32- Postconditions:XX::const_pointer
static_cast<XX::const_pointer>(x) == q
.pointer_traits<XX::pointer>::pointer_to(r)-33- Result:
-34- Postconditions: Same asXX::pointer
p
.a.allocate(n)-35- Result:
[…]XX::pointer
a.allocate(n, y)-40- Result:
[…]XX::pointer
a.allocate_at_least(n)[…]-43- Result:
-44- Returns:allocation_result<XX::pointer>
allocation_result<XX::pointer>{ptr, count}
whereptr
is memory allocated for an array of countT
and such an object is created but array elements are not constructed, such thatcount = n
. Ifn == 0
, the return value is unspecified. […]a.max_size()[…]-50- Result:
-51- Returns: The largest valueXX::size_type
n
that can meaningfully be passed to. -52- Remarks: Default:
X::a.allocate(n)numeric_limits<size_type>::max() / sizeof(value_type)
a == b[…]-59- Result:
-60- Returns:bool
a == YY::rebind_alloc<T>
.::other(b)-92- An allocator type
-93- LetX
shall meet the Cpp17CopyConstructible requirements (Table 33). TheXX::pointer
,XX::const_pointer
,XX::void_pointer
, andXX::const_void_pointer
types shall meet the Cpp17NullablePointer requirements (Table 37). No constructor, comparison operator function, copy operation, move operation, or swap operation on these pointer types shall exit via an exception.XX::pointer
andXX::const_pointer
shall also meet the requirements for a Cpp17RandomAccessIterator (25.3.5.7) and the additional requirement that, whenand
ap(
are dereferenceable pointer values for some integral valueap + n)n
,addressof(*(
isap + n)) == addressof(*ap) + ntrue
.x1
andx2
denote objects of (possibly different) typesXX::void_pointer
,XX::const_void_pointer
,XX::pointer
, orXX::const_pointer
. Then,x1
andx2
are equivalently-valued pointer values, if and only if bothx1
andx2
can be explicitly converted to the two corresponding objectspx1
andpx2
of typeXX::const_pointer
, using a sequence ofstatic_cast
s using only these four types, and the expressionpx1 == px2
evaluates totrue
. -94- Letw1
andw2
denote objects of typeXX::void_pointer
. Then for the expressionsw1 == w2 w1 != w2either or both objects may be replaced by an equivalently-valued object of type
-95- LetXX::const_void_pointer
with no change in semantics.p1
andp2
denote objects of typeXX::pointer
. Then for the expressionsp1 == p2 p1 != p2 p1 < p2 p1 <= p2 p1 >= p2 p1 > p2 p1 - p2either or both objects may be replaced by an equivalently-valued object of type
XX::const_pointer
with no change in semantics.