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

### 3677. Is a cv-qualified `pair` specially handled in uses-allocator construction?

Section: 20.2.8.2 [allocator.uses.construction] Status: WP Submitter: Jiang An Opened: 2022-02-16 Last modified: 2022-11-17

Priority: 2

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

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

View all issues with WP status.

Discussion:

It seems unclear whether cv-qualified `pair` specializations are considered as specializations of `pair` in 20.2.8.2 [allocator.uses.construction].

Currently MSVC STL only considered cv-unqualified pair types as such specializations, while libstdc++ accept both cv-unqualified and `const`-qualified pair types as such specializations. The resolution of LWG 3525 uses `remove_cv_t`, which possibly imply that the specialization of pair may be cv-qualified.

The difference can be observed via the following program:

>     #include <utility>
>     #include <memory>
>     #include <vector>
>     #include <cassert>

>     template<class T>
>     class payload_ator {

>       int payload{};

>     public:
>       payload_ator() = default;

>       constexpr explicit payload_ator(int n) noexcept : payload{n} {}

>       template<class U>
>       constexpr explicit payload_ator(payload_ator<U> a) noexcept : payload{a.payload} {}

>       friend bool operator==(payload_ator, payload_ator) = default;

>       template<class U>
>       friend constexpr bool operator==(payload_ator x, payload_ator<U> y) noexcept
>       {
>         return x.payload == y.payload;
>       }

>       using value_type = T;

>       constexpr T* allocate(std::size_t n) { return std::allocator<T>{}.allocate(n); }

>       constexpr void deallocate(T* p, std::size_t n) { return std::allocator<T>{}.deallocate(p, n); }

>       constexpr int get_payload() const noexcept { return payload; }
>     };

>     bool test()
>     {
>       constexpr int in_v = 42;
>       using my_pair_t = std::pair<int, std::vector<int, payload_ator<int>>>;
>       auto out_v = std::make_obj_using_allocator<const my_pair_t>(payload_ator<int>{in_v}).second.get_allocator().get_payload();
>       return in_v == out_v;
>     }

>     int main()
>     {
>       assert(test()); // passes only if a const-qualified pair specialization is considered as a pair specialization
>     }

[2022-03-04; Reflector poll]

Set priority to 2 after reflector poll.

[2022-08-24; LWG telecon]

Change every `T` to `remove_cv_t<T>`.

[2022-08-25; Jonathan Wakely provides wording]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4910.

> - Modify 20.2.8.2 [allocator.uses.construction] as indicated, using `remove_cv_t` in every Constraints: element:

>   > Constraints: `remove_cv_t<T>` [is|is not] a specialization of `pair`

[2022-09-23; Jonathan provides improved wording]

[2022-09-30; moved to Tentatively Ready after seven votes in reflector poll]

[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4917.

1. Modify 20.2.8.2 [allocator.uses.construction] as indicated, using `remove_cv_t` in every Constraints: element (paragraphs 4, 6, 8, 10, 12, 14, 17):

   > Constraints: `remove_cv_t<T>` [is|is not] a specialization of `pair`

2. Add `remove_cv_t` in paragraph 5:

   > -5- Returns: A `tuple` value determined as follows:

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

   > (5.2) — Otherwise, if `uses_allocator_v<remove_cv_t<T>, 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)...)

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

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

3. Rephrase paragraph 7 in terms of the pair member types:

   > -?- Let `T1` be `T::first_type`. Let `T2` be `T::second_type`.

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

   > -7- Effects:: For `T` specified as `pair<T1, T2>`, equivalent Equivalent to: