Issue 2586: Wrong value category used in scoped_allocator_adaptor::construct() (original) (raw)
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.6.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.6.4 [allocator.adaptor.members] p9 says that the is_constructibletests are done using inner_allocator_type, which checks for construction from an rvalue, but then the constructor is passedinner_allocator() which returns a non-const lvalue reference. The value categories should be consistent, otherwise this fails to compile:
#include #include
struct X { using allocator_type = std::allocator; X(std::allocator_arg_t, allocator_type&&) { } X(allocator_type&) { } };
int main() { std::scoped_allocator_adaptor<std::allocator> 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 itsa.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 theis_constructible traits and the initialization expressions match. However, it does say "an allocator alloc of type Alloc" and if Allocis 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]:
- Change 20.2.8.2 [allocator.uses.construction] p1:
-1- Uses-allocator construction with allocator
Allocrefers to the construction of an objectobjof typeT, using constructor argumentsv1, v2, ..., vNof typesV1, V2, ..., VN, respectively, and an allocator (or reference to an allocator)allocof typeAlloc, according to the following rules:- Change the 2nd and 3rd bullets in 20.6.4 [allocator.adaptor.members] p9 to add two lvalue-references:
- (9.2) — Otherwise, if
uses_allocator<T, inner_allocator_type>::valueistrueandis_constructible<T, allocator_arg_t, inner_allocator_type&, Args...>::valueistrue, calls_OUTERMOSTALLOCTRAITS_(*this)::construct(_OUTERMOST_(*this), p, allocator_arg, inner_allocator(), std::forward<Args>(args)...).- (9.3) — Otherwise, if
uses_allocator<T, inner_allocator_type>::valueistrueandis_constructible<T, Args..., inner_allocator_type&>::valueistrue, calls_OUTERMOSTALLOCTRAITS_(*this)::construct(_OUTERMOST_(*this), p, std::forward<Args>(args)..., inner_allocator()).- Change the 2nd, 3rd, 6th, and 7th bullets in 20.6.4 [allocator.adaptor.members] p11 to add four lvalue-references:
- (11.2) — Otherwise, if
uses_allocator<T1, inner_allocator_type>::valueistrueandis_constructible<T1, allocator_arg_t, inner_allocator_type&, Args1...>::valueistrue, thenxprimeistuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(x)).- (11.3) — Otherwise, if
uses_allocator<T1, inner_allocator_type>::valueistrueandis_constructible<T1, Args1..., inner_allocator_type&>::valueistrue, thenxprimeistuple_cat(std::move(x), tuple<inner_allocator_type&>(inner_allocator())).- […]
- (11.6) — Otherwise, if
uses_allocator<T2, inner_allocator_type>::valueistrueandis_constructible<T2, allocator_arg_t, inner_allocator_type&, Args2...>::valueistrue, thenyprimeistuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(y)).- (11.7) — Otherwise, if
uses_allocator<T2, inner_allocator_type>::valueistrueandis_constructible<T2, Args2..., inner_allocator_type&>::valueistrue, thenyprimeistuple_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.
- Change the 2nd and 3rd bullets in 20.6.4 [allocator.adaptor.members] p9 to add two lvalue-references:
- (9.2) — Otherwise, if
uses_allocator<T, inner_allocator_type>::valueistrueandis_constructible<T, allocator_arg_t, inner_allocator_type&, Args...>::valueistrue, calls_OUTERMOSTALLOCTRAITS_(*this)::construct(_OUTERMOST_(*this), p, allocator_arg, inner_allocator(), std::forward<Args>(args)...). - (9.3) — Otherwise, if
uses_allocator<T, inner_allocator_type>::valueistrueandis_constructible<T, Args..., inner_allocator_type&>::valueistrue, calls_OUTERMOSTALLOCTRAITS_(*this)::construct(_OUTERMOST_(*this), p, std::forward<Args>(args)..., inner_allocator()).
- (9.2) — Otherwise, if
- Change the 2nd, 3rd, 6th, and 7th bullets in 20.6.4 [allocator.adaptor.members] p11 to add four lvalue-references:
- (11.2) — Otherwise, if
uses_allocator<T1, inner_allocator_type>::valueistrueandis_constructible<T1, allocator_arg_t, inner_allocator_type&, Args1...>::valueistrue, thenxprimeistuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(x)). - (11.3) — Otherwise, if
uses_allocator<T1, inner_allocator_type>::valueistrueandis_constructible<T1, Args1..., inner_allocator_type&>::valueistrue, thenxprimeistuple_cat(std::move(x), tuple<inner_allocator_type&>(inner_allocator())). - […]
- (11.6) — Otherwise, if
uses_allocator<T2, inner_allocator_type>::valueistrueandis_constructible<T2, allocator_arg_t, inner_allocator_type&, Args2...>::valueistrue, thenyprimeistuple_cat(tuple<allocator_arg_t, inner_allocator_type&>(allocator_arg, inner_allocator()), std::move(y)). - (11.7) — Otherwise, if
uses_allocator<T2, inner_allocator_type>::valueistrueandis_constructible<T2, Args2..., inner_allocator_type&>::valueistrue, thenyprimeistuple_cat(std::move(y), tuple<inner_allocator_type&>(inner_allocator())).
- (11.2) — Otherwise, if