Title
Wrong value category used in scoped_allocator_adaptor::construct()
Status
c++17
Section
[allocator.adaptor.members][allocator.uses.construction]
Submitter
Jonathan Wakely

Created on 2016-01-15.00:00:00 last changed 81 months ago

Messages

Date: 2016-02-07.20:24:45

Proposed resolution:

This wording is relative to N4567.

  1. Change the 2nd and 3rd bullets in [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 [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())).

Date: 2016-02-07.20:24:45

[ 2016-02, Issues Telecon ]

P0; move to Tentatively Ready.

Date: 2016-02-07.20:24:45

[ 2016-02, Issues Telecon ]

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

Original Resolution [SUPERSEDED]:
  1. Change [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 [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 [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())).

Date: 2016-01-15.00:00:00

[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 [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.

History
Date User Action Args
2017-07-30 20:15:43adminsetstatus: wp -> c++17
2016-03-07 04:11:48adminsetstatus: ready -> wp
2016-02-07 20:24:45adminsetmessages: + msg7975
2016-02-07 20:24:45adminsetmessages: + msg7974
2016-02-07 20:24:45adminsetstatus: new -> ready
2016-01-17 21:34:34adminsetmessages: + msg7690
2016-01-15 00:00:00admincreate