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

### 2586. Wrong value category used in `scoped_allocator_adaptor::construct()`

Section: 20.5.4 [allocator.adaptor.members], 20.2.8.2 [allocator.uses.construction] Status: C++17 Submitter: Jonathan Wakely Opened: 2016-01-15 Last modified: 2017-07-30

Priority: 0

View all other issues in [allocator.adaptor.members].

View all issues with C++17 status.

Discussion:

20.5.4 [allocator.adaptor.members] p9 says that the `is_constructible` tests are done using `inner_allocator_type`, which checks for construction from an rvalue, but then the constructor is passed `inner_allocator()` which returns a non-`const` lvalue reference. The value categories should be consistent, otherwise this fails to compile:

>     #include <memory>
>     #include <scoped_allocator>

>     struct X {
>       using allocator_type = std::allocator<X>;
>       X(std::allocator_arg_t, allocator_type&&) { }
>       X(allocator_type&) { }
>     };

>     int main() {
>       std::scoped_allocator_adaptor<std::allocator<X>> sa;
>       sa.construct(sa.allocate(1));
>     }

`uses_allocator<X, decltype(sa)::inner_allocator_type>>` is true, because it can be constructed from an rvalue of the allocator type, so bullet (9.1) doesn't apply.

`is_constructible<X, allocator_arg_t, decltype(sa)::inner_allocator_type>` is true, so bullet (9.2) applies. That means we try to construct the object passing it `sa.inner_allocator()` which is an lvalue reference, so it fails.

The `is_constructible` checks should use an lvalue reference, as that's what's actually going to be used.

I don't think the same problem exists in the related wording in 20.2.8.2 [allocator.uses.construction] if we assume that the value categories of `v1, v2, ..., vN` and `alloc` are meant to be preserved, so that the `is_constructible` traits and the initialization expressions match. However, it does say "an allocator `alloc` of type `Alloc`" and if `Alloc` is an reference type then it's not an allocator, so I suggest a small tweak there too.

[2016-02, Issues Telecon]

Strike first paragraph of PR, and move to Tentatively Ready.

Original Resolution [SUPERSEDED]:

> 1. Change 20.2.8.2 [allocator.uses.construction] p1:

>    > -1- Uses-allocator construction with allocator `Alloc` refers to the construction of an object `obj` of type `T`, using constructor arguments `v1, v2, ..., vN` of types `V1, V2, ..., VN`, respectively, and an allocator (or reference to an allocator) `alloc` of type `Alloc`, according to the following rules:

> 2. Change the 2nd and 3rd bullets in 20.5.4 [allocator.adaptor.members] p9 to add two lvalue-references:

>    > 1. (9.2) — Otherwise, if `uses_allocator<T, inner_allocator_type>::value` is `true` and `is_constructible<T, allocator_arg_t, inner_allocator_type&, Args...>::value` is `true`, calls `OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST(*this), p, allocator_arg, inner_allocator(), std::forward<Args>(args)...)`.

>    > 2. (9.3) — Otherwise, if `uses_allocator<T, inner_allocator_type>::value` is `true` and `is_constructible<T, Args..., inner_allocator_type&>::value` is `true`, calls `OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST(*this), p, std::forward<Args>(args)..., inner_allocator())`.

> 3. Change the 2nd, 3rd, 6th, and 7th bullets in 20.5.4 [allocator.adaptor.members] p11 to add four lvalue-references:

>    > 1. (11.2) — Otherwise, if `uses_allocator<T1, inner_allocator_type>::value` is `true` and `is_constructible<T1, allocator_arg_t, inner_allocator_type&, Args1...>::value` is `true`, then `xprime` is `tuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(x))`.

>    > 2. (11.3) — Otherwise, if `uses_allocator<T1, inner_allocator_type>::value` is `true` and `is_constructible<T1, Args1..., inner_allocator_type&>::value` is `true`, then `xprime` is `tuple_cat(std::move(x), tuple<inner_allocator_type&>(inner_allocator()))`.

>    > 3. […]

>    > 4. (11.6) — Otherwise, if `uses_allocator<T2, inner_allocator_type>::value` is `true` and `is_constructible<T2, allocator_arg_t, inner_allocator_type&, Args2...>::value` is `true`, then `yprime` is `tuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(y))`.

>    > 5. (11.7) — Otherwise, if `uses_allocator<T2, inner_allocator_type>::value` is `true` and `is_constructible<T2, Args2..., inner_allocator_type&>::value` is `true`, then `yprime` is `tuple_cat(std::move(y), tuple<inner_allocator_type&>(inner_allocator()))`.

[2016-02, Issues Telecon]

P0; move to Tentatively Ready.

Proposed resolution:

This wording is relative to N4567.

1. Change the 2nd and 3rd bullets in 20.5.4 [allocator.adaptor.members] p9 to add two lvalue-references:

   > 1. (9.2) — Otherwise, if `uses_allocator<T, inner_allocator_type>::value` is `true` and `is_constructible<T, allocator_arg_t, inner_allocator_type&, Args...>::value` is `true`, calls `OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST(*this), p, allocator_arg, inner_allocator(), std::forward<Args>(args)...)`.

   > 2. (9.3) — Otherwise, if `uses_allocator<T, inner_allocator_type>::value` is `true` and `is_constructible<T, Args..., inner_allocator_type&>::value` is `true`, calls `OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST(*this), p, std::forward<Args>(args)..., inner_allocator())`.

2. Change the 2nd, 3rd, 6th, and 7th bullets in 20.5.4 [allocator.adaptor.members] p11 to add four lvalue-references:

   > 1. (11.2) — Otherwise, if `uses_allocator<T1, inner_allocator_type>::value` is `true` and `is_constructible<T1, allocator_arg_t, inner_allocator_type&, Args1...>::value` is `true`, then `xprime` is `tuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(x))`.

   > 2. (11.3) — Otherwise, if `uses_allocator<T1, inner_allocator_type>::value` is `true` and `is_constructible<T1, Args1..., inner_allocator_type&>::value` is `true`, then `xprime` is `tuple_cat(std::move(x), tuple<inner_allocator_type&>(inner_allocator()))`.

   > 3. […]

   > 4. (11.6) — Otherwise, if `uses_allocator<T2, inner_allocator_type>::value` is `true` and `is_constructible<T2, allocator_arg_t, inner_allocator_type&, Args2...>::value` is `true`, then `yprime` is `tuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(y))`.

   > 5. (11.7) — Otherwise, if `uses_allocator<T2, inner_allocator_type>::value` is `true` and `is_constructible<T2, Args2..., inner_allocator_type&>::value` is `true`, then `yprime` is `tuple_cat(std::move(y), tuple<inner_allocator_type&>(inner_allocator()))`.