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

### 2969. `polymorphic_allocator::construct()` shouldn't pass `resource()`

Section: 20.12.3.3 [mem.poly.allocator.mem] Status: C++20 Submitter: Pablo Halpern Opened: 2017-05-30 Last modified: 2021-02-25

Priority: 2

View all other issues in [mem.poly.allocator.mem].

View all issues with C++20 status.

Discussion:

Section 20.12.3.3 [mem.poly.allocator.mem] defines the effect of `polymorphic_allocator<T>::construct` as:

> Effects: Construct a `T` object in the storage whose address is represented by `p` by uses-allocator construction with allocator `resource()` and constructor arguments `std::forward<Args>(args)...`.

The use of `resource()` is a hold-over from the LFTS, which contains a modified definition of uses-allocator construction. This revised definition was not carried over into the C++17 WP when allocator_resource and polymorphic_allocator were moved over.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4659.

> 1. Edit 20.12.3.3 [mem.poly.allocator.mem] as indicated:

>    >     template <class T, class... Args>
>    >       void construct(T* p, Args&&... args);

>    > > -5- Requires: Uses-allocator construction of `T` with allocator `resource()*this` (see 20.10.7.2 [allocator.uses.construction]) and constructor arguments `std::forward<Args>(args)...` is well-formed. [Note: Uses-allocator construction is always well formed for types that do not use allocators. — end note]

>    > > -6- Effects: Construct a `T` object in the storage whose address is represented by `p` by uses-allocator construction with allocator `resource()*this` and constructor arguments `std::forward<Args>(args)...`.

>    > > -7- Throws: Nothing unless the constructor for `T` throws.

>    >     template <class T1, class T2, class... Args1, class... Args2>
>    >       void construct(pair<T1,T2>* p, piecewise_construct_t,
>    >                      tuple<Args1...> x, tuple<Args2...> y);

>    > > -8- [Note: This method and the `construct` methods that follow are overloads for piecewise construction of pairs (20.4.2 [pairs.pair]). — end note]

>    > > -9- Effects: Let `xprime` be a `tuple` constructed from `x` according to the appropriate rule from the following list. [Note: The following description can be summarized as constructing a `pair<T1, T2>` object in the storage whose address is represented by `p`, as if by separate uses-allocator construction with allocator `resource()*this` (20.10.7.2 [allocator.uses.construction]) of `p->first` using the elements of `x` and `p->second` using the elements of `y`. — end note]

>    > > […]

[2017-06-12, Pablo comments]

The current description is correct and does not depend on changes to uses-allocator construction. It relies on the fact that `memory_resource*` is convertible to `polymorphic_allocator`.

[2017-06-13, Tim Song reopens]

While it is true that `memory_resource*` is convertible to `polymorphic_allocator`, uses-allocator construction still requires allocators, and a `memory_resource*` isn't an allocator.

To take a concrete example from the current WP, a `pmr::vector<std::promise<int>>`, as specified, will be attempting to uses-allocator construct a `promise<int>` with a `memory_resource*`, but `std::promise`'s allocator-taking constructor expects something that satisfies the allocator requirements, rather than a `memory_resource*`.

[2017-06-13, Daniel and Tim restore and improve the previously proposed wording]

[2017-07 Toronto Monday issue prioritization]

Priority 2; Dietmar to check the P/R before Albuquerque.

[2017-11 Albuquerque Wednesday issue processing]

Move to Ready.

[2018-3-17 Adopted in Jacksonville]

Proposed resolution:

This wording is relative to N4659.

1. Edit 20.12.3.3 [mem.poly.allocator.mem] as indicated:

   >     template <class T, class... Args>
   >       void construct(T* p, Args&&... args);

   > > -5- Requires: Uses-allocator construction of `T` with allocator `resource()*this` (see 20.10.7.2 [allocator.uses.construction]) and constructor arguments `std::forward<Args>(args)...` is well-formed. [Note: Uses-allocator construction is always well formed for types that do not use allocators. — end note]

   > > -6- Effects: Construct a `T` object in the storage whose address is represented by `p` by uses-allocator construction with allocator `resource()*this` and constructor arguments `std::forward<Args>(args)...`.

   > > -7- Throws: Nothing unless the constructor for `T` throws.

   >     template <class T1, class T2, class... Args1, class... Args2>
   >       void construct(pair<T1,T2>* p, piecewise_construct_t,
   >                      tuple<Args1...> x, tuple<Args2...> y);

   > > -8- [Note: This method and the `construct` methods that follow are overloads for piecewise construction of pairs (20.4.2 [pairs.pair]). — end note]

   > > -9- Effects: Let `xprime` be a `tuple` constructed from `x` according to the appropriate rule from the following list. [Note: The following description can be summarized as constructing a `pair<T1, T2>` object in the storage whose address is represented by `p`, as if by separate uses-allocator construction with allocator `resource()*this` (20.10.7.2 [allocator.uses.construction]) of `p->first` using the elements of `x` and `p->second` using the elements of `y`. — end note]

   > > 1. (9.1) — If `uses_allocator_v<T1,memory_resource*polymorphic_allocator>` is `false` and `is_constructible_v<T1,Args1...>` is `true`, then `xprime` is `x`.

   > > 2. (9.2) — Otherwise, if `uses_allocator_v<T1,memory_resource*polymorphic_allocator>` is `true` and `is_constructible_v<T1,allocator_arg_t,memory_resource*polymorphic_allocator,Args1...>` is `true`, then `xprime` is `tuple_cat(make_tuple(allocator_arg, resource()*this), std::move(x))`.

   > > 3. (9.3) — Otherwise, if `uses_allocator_v<T1,memory_resource*polymorphic_allocator>` is `true` and `is_constructible_v<T1,Args1...,memory_resource*polymorphic_allocator>` is `true`, then `xprime` is `tuple_cat(std::move(x), make_tuple(resource()*this))`.

   > > 4. (9.4) — Otherwise the program is ill formed.

   > > Let `yprime` be a tuple constructed from `y` according to the appropriate rule from the following list:

   > > 1. (9.5) — If `uses_allocator_v<T2,memory_resource*polymorphic_allocator>` is `false` and `is_constructible_v<T2,Args2...>` is `true`, then `yprime` is `y`.

   > > 2. (9.6) — Otherwise, if `uses_allocator_v<T2,memory_resource*polymorphic_allocator>` is `true` and `is_constructible_v<T2,allocator_arg_t,memory_resource*polymorphic_allocator,Args2...>` is `true`, then `yprime` is `tuple_cat(make_tuple(allocator_arg, resource()*this), std::move(y))`.

   > > 3. (9.7) — Otherwise, if `uses_allocator_v<T2,memory_resource*polymorphic_allocator>` is `true` and `is_constructible_v<T2,Args2...,memory_resource*polymorphic_allocator>` is `true`, then `yprime` is `tuple_cat(std::move(y), make_tuple(resource()*this))`.

   > > 4. (9.8) — Otherwise the program is ill formed.