allocator<void>
and possibly polymorphic_allocator<void>
should be clarifiedSection: 20.2.10 [default.allocator], 20.4.3 [mem.poly.allocator.class] Status: New Submitter: Daniel Krügler Opened: 2023-04-08 Last modified: 2023-05-24
Priority: 3
View other active issues in [default.allocator].
View all other issues in [default.allocator].
View all issues with New status.
Discussion:
Before P0174 had been approved for the working paper, the validity of using void
as template argument for
std::allocator
was obvious due to the existing specification of the explicit specialization allocator<void>
.
std::allocator
to support void
as template argument
any more. We fall now under the constraints for template "components" specified in 16.4.5.8 [res.on.functions] bullet 2.5.
-2- In particular, the behavior is undefined in the following cases:
[…]
(2.5) — If an incomplete type (6.8.1 [basic.types.general]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.
But no such allowance wording exists for allocator<void>
nor for polymorphic_allocator<void>
, more
to the contrary, 16.4.4.6.1 [allocator.requirements.general] only refers to cv-unqualified object types as value types
and void
is not an object type.
operator new
members of std::generator
mention a fall-back of using allocator<void>
.
20.2.10.1 [default.allocator.general] says that all specializations of the default allocator meet the allocator completeness
requirements (16.4.4.6.2 [allocator.requirements.completeness]), but albeit this specification does not specifically exclude
the existence of an incomplete value type, the wording here does also not provide a definite statement, that it is valid (The
wording originally was provided when we started adding support for (yet) incomplete value types that at some point later will become
complete, but void
can never be completed), since it is mostly focused on the completeness requirement for the allocator
type itself.
The situation is similar (albeit maybe not that strong) for polymorphic_allocator<void>
, since
20.4.3 [mem.poly.allocator.class] p1 has some unusual wording form that says
-1- A specialization of class template
pmr::polymorphic_allocator
meets the Cpp17Allocator requirements (16.4.4.6.1 [allocator.requirements.general]) if its template argument is a cv-unqualified object type.
and says then in p2:
-2- A specialization of class template
pmr::polymorphic_allocator
meets the allocator completeness requirements (16.4.4.6.2 [allocator.requirements.completeness]) if its template argument is a cv-unqualified object type.
Again, this wording is not conclusive, whether void
is intended to be supported, it is certainly not completely ruled
out, but that is not strong enough to counterpart 16.4.5.8 [res.on.functions] (2.5). It is maybe worth pointing out that
for a while we were considering to use void
as default template argument for pmr::polymorphic_allocator
,
see e.g. P0339R0, but that thought was later replaced by deciding for std::byte
instead, which
is a complete object type.
std::allocator
is intended to support incomplete types, maybe also
for polymorphic_allocator
. If polymorphic_allocator
is intended to support incomplete types as well,
we should also amend 20.4.3.3 [mem.poly.allocator.mem] p1 and p8 with a Mandates: element similarly as we did
for std::allocator
via LWG 3307.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4944.
Add the following new paragraph at the end of 20.2.10.1 [default.allocator.general] as indicated:
-2-
-?- The template parameterallocator_traits<allocator<T>>::is_always_equal::value
istrue
for anyT
.T
ofallocator
may be an incomplete type.
Add the following new paragraph at the end of 20.4.3.1 [mem.poly.allocator.class.general] (possibly just after the class template synopsis) as indicated:
-?- The template parameter
Tp
ofpolymorphic_allocator
may be an incomplete type.
Modify 20.4.3.3 [mem.poly.allocator.mem] as indicated:
[Drafting note: The reference to
sizeof(Tp)
gives indirect evidence that we want to exclude incomplete types here, but we cannot rely on the "equivalent to" magic formula, because that is defined conditionally]
[[nodiscard]] Tp* allocate(size_t n);-?- Mandates:
-1- Effects: IfTp
is not an incomplete type (6.8.1 [basic.types.general]).numeric_limits<size_t>::max() / sizeof(Tp) < n
, throwsbad_array_new_length
. Otherwise equivalent to:return static_cast<Tp*>(memory_rsrc->allocate(n * sizeof(Tp), alignof(Tp)));[Drafting note: We don't need extra wording for the member templates
Forallocate_object
,deallocate_object
,new_object
,delete_object
, orconstruct
, because their semantics does not depend on template parameterTp
and the general wording of 16.4.5.8 [res.on.functions] (2.5) again requires the completeness ofT
here.deallocate
we also omit the completeness requirement (as we did so forallocator::deallocate
), because this is indirectly implied by the existing precondition. ]