This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3192. §[allocator.uses.construction] functions misbehave for `const` types

Section: 20.2.8.2 [allocator.uses.construction] Status: New Submitter: Jonathan Wakely Opened: 2019-02-28 Last modified: 2020-05-01

Priority: 3

View other active issues in [allocator.uses.construction].

View all other issues in [allocator.uses.construction].

View all issues with New status.

Discussion:

The new functions added by P0591R4 misbehave for cv-qualified types. A specialization `std::uses_allocator<X, Alloc>` will not match `const X`, so `std::uses_allocator_construction_args<const X>` will return a different result from `std::uses_allocator_construction_args<X>`. It makes no sense to construct `X` and `const X` differently, either the type wants to use an allocator or it doesn't. I think `std::uses_allocator_construction_args<T>` should remove cv-qualifiers before checking `uses_allocator`, so that it works consistently.

We could consider changing `std::make_obj_using_allocator` to also strip cv-qualifiers, but it's not necessary as C++17 guaranteed elision works even for prvalues of `const` types. We only need to make the construction args ignore cv-qualifiers. We don't want to make cv-qualified types ill-formed, because that would require users of uses-allocator construction to strip cv-qualifiers before using these functions, e.g. in cases like `std::tuple<const int> t(allocator_arg, alloc, 1);`

[2019-03-15 Priority set to 3 after reflector discussion]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4800.

> 1. Change 20.2.8.2 [allocator.uses.construction] as indicated:

>    >     template <class T, class Alloc, class... Args>
>    >       auto uses_allocator_construction_args(const Alloc& alloc, Args&&... args) -> see below;

>    > > -4- Constraints: `T` is not a specialization of `pair`.

>    > > -5- Returns: A `tuple` value determined as follows, where `U` denotes the type `remove_cv_t<T>`:

>    > > 1. (5.1) — If `uses_allocator_v<TU, Alloc>` is `false` and `is_constructible_v<T, Args...>` is `true`, return `forward_as_tuple(std::forward<Args>(args)...)`.

>    > > 2. (5.2) — Otherwise, if `uses_allocator_v<TU, Alloc>` is `true` and `is_constructible_v<T, allocator_arg_t, Alloc, Args...>` is `true`, return

>    > >    >     tuple<allocator_arg_t, const Alloc&, Args&&...>(
>    > >    >       allocator_arg, alloc, std::forward<Args>(args)...)

>    > > 3. (5.3) — Otherwise, if `uses_allocator_v<TU, Alloc>` is `true` and `is_constructible_v<T, Args..., Alloc>` is `true`, return `forward_as_tuple(std::forward<Args>(args)..., alloc)`.

>    > > 4. (5.4) — Otherwise, the program is ill-formed.

>    > […]

>    >     template <class T, class Alloc, class Tuple1, class Tuple2>
>    >       auto uses_allocator_construction_args(const Alloc& alloc, piecewise_construct_t,
>    >                                             Tuple1&& x, Tuple2&& y) -> see below;

>    > > -6- Constraints: `T` is a specialization of `pair`.

>    > > -7- Effects: For `T` specified as (possibly `const`) `pair<T1, T2>`, equivalent to:

>    > > […]

>    >     template <class T, class Alloc, class... Args>
>    >       T* uninitialized_construct_using_allocator(T* p, const Alloc& alloc, Args&&... args);

>    > > -17- Effects: Equivalent to:

>    > > >     return ::new(static_cast<void*>voidify(*p))
>    > > >       T(make_obj_using_allocator<T>(alloc, std::forward<Args>(args)...));

[2020-05-01; Daniel syncs wording with recent working draft]

The previously needed change for `uninitialized_construct_using_allocator` is no longer required, because the reworded call to `construct_at` does do the right thing now.

Proposed resolution:

This wording is relative to N4861.

1. Change 20.2.8.2 [allocator.uses.construction] as indicated:

   >     template <class T, class Alloc, class... Args>
   >       constexpr auto uses_allocator_construction_args(const Alloc& alloc,
   >                                             Args&&... args) noexcept -> see below;

   > > -4- Constraints: `T` is not a specialization of `pair`.

   > > -5- Returns: A `tuple` value determined as follows, where `U` denotes the type `remove_cv_t<T>`:

   > > 1. (5.1) — If `uses_allocator_v<TU, Alloc>` is `false` and `is_constructible_v<T, Args...>` is `true`, return `forward_as_tuple(std::forward<Args>(args)...)`.

   > > 2. (5.2) — Otherwise, if `uses_allocator_v<TU, Alloc>` is `true` and `is_constructible_v<T, allocator_arg_t, const Alloc&, Args...>` is `true`, return

   > >    >     tuple<allocator_arg_t, const Alloc&, Args&&...>(
   > >    >       allocator_arg, alloc, std::forward<Args>(args)...)

   > > 3. (5.3) — Otherwise, if `uses_allocator_v<TU, Alloc>` is `true` and `is_constructible_v<T, Args..., const Alloc&>` is `true`, return `forward_as_tuple(std::forward<Args>(args)..., alloc)`.

   > > 4. (5.4) — Otherwise, the program is ill-formed.

   > […]

   >     template <class T, class Alloc, class Tuple1, class Tuple2>
   >       constexpr auto uses_allocator_construction_args(const Alloc& alloc, piecewise_construct_t,
   >                                             Tuple1&& x, Tuple2&& y)
   >                                             noexcept -> see below;

   > > -6- Constraints: `T` is a specialization of `pair`.

   > > -7- Effects: For `T` specified as (possibly `const`) `pair<T1, T2>`, equivalent to:

   > > […]