polymorphic_allocator::construct()
should more closely match scoped_allocator_adaptor::construct()
Section: 20.4.3.3 [mem.poly.allocator.mem] Status: Resolved Submitter: Arthur O'Dwyer Opened: 2018-05-14 Last modified: 2020-09-06
Priority: 3
View all other issues in [mem.poly.allocator.mem].
View all issues with Resolved status.
Discussion:
Based on my new understanding of how uses_allocator
and is_constructible
are supposed to play together,
I think there was a minor defect in the resolution of LWG 2969 "polymorphic_allocator::construct()
shouldn't pass resource()
".
is_constructible
in 20.4.3.3 [mem.poly.allocator.mem] (10.2), which were added to resolve LWG
2969, are missing an lvalue-qualification to match the lvalue-qualification of the parallel calls in
[llocator.adaptor.member] (9.2).
The latter talks about constructibility from inner_allocator_type&
; the former (after LWG 2969) talks about
constructibility from polymorphic_allocator
with no ref-qualification. But since we are perfect-forwarding
*this
through a tuple of references, we definitely are going to try to construct the target object from
a polymorphic_allocator&
and not from a polymorphic_allocator
. I believe that the wording in
20.4.3.3 [mem.poly.allocator.mem] (10.2) needs to be updated to make the program ill-formed in cases where
that construction is going to fail.
Orthogonally, I believe we need additional std::move
's in the sentence following 20.4.3.3 [mem.poly.allocator.mem] (10.8)
for two reasons:
We don't want to copy-construct the user's arguments that came in
by-value as part of x
and/or y
.
The value category of the argument to pair
's constructor is currently
unspecified; it could reasonably be either an xvalue or a prvalue. Adding the std::move
makes it clearer
that we really want it to be an xvalue.
[2018-06-18 after reflector discussion]
Priority set to 3
[2019-02; Kona Wednesday night issue processing]
This was resolved by the adoption of P0591 in San Diego.
Proposed resolution:
This wording is relative to N4750.
Edit 20.4.3.3 [mem.poly.allocator.mem] as indicated:
template<class T1, class T2, class... Args1, class... Args2> void construct(pair<T1, T2>* p, piecewise_construct_t, tuple<Args1...> x, tuple<Args2...> y);-9- […]
-10- Effects:: […]
(10.1) — […]
(10.2) — Otherwise, if
uses_allocator_v<T1,polymorphic_allocator>
istrue
andis_constructible_v<T1, allocator_arg_t, polymorphic_allocator&, Args1...>
istrue
, thenxprime
istuple_cat(
.make_tupletuple<allocator_arg_t, polymorphic_allocator&>(allocator_arg, *this), std::move(x))(10.3) — Otherwise, if
uses_allocator_v<T1, polymorphic_allocator>
istrue
andis_constructible_v<T1, Args1..., polymorphic_allocator&>
istrue
, thenxprime
istuple_cat(std::move(x),
.make_tupletuple<polymorphic_allocator&>(*this))(10.4) — Otherwise the program is ill formed.
Let
yprime
be a tuple constructed fromy
according to the appropriate rule from the following list:
(10.5) — […]
(10.6) — Otherwise, if
uses_allocator_v<T2, polymorphic_allocator>
istrue
andis_constructible_v<T2, allocator_arg_t, polymorphic_allocator&, Args2...>
istrue
, thenyprime
istuple_cat(
.make_tupletuple<allocator_arg_t, polymorphic_allocator&>(allocator_arg, *this), std::move(y))(10.7) — Otherwise, if
uses_allocator_v<T2, polymorphic_allocator>
istrue
andis_constructible_v<T2, Args2..., polymorphic_allocator&>
istrue
, thenyprime
istuple_cat(std::move(y),
.make_tupletuple<polymorphic_allocator&>(*this))(10.8) — Otherwise the program is ill formed.
Then, using
piecewise_construct
,std::move(xprime)
, andstd::move(yprime)
as the constructor arguments, this function constructs apair<T1, T2>
object in the storage whose address is represented byp
.