2203. scoped_allocator_adaptor uses wrong argument types for piecewise construction

Section: 20.5.4 [allocator.adaptor.members] Status: C++14 Submitter: Jonathan Wakely Opened: 2012-10-19 Last modified: 2016-01-28

Priority: Not Prioritized

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

View all issues with C++14 status.

Discussion:

In 20.5.4 [allocator.adaptor.members] paragraph 11 the effects clause says a tuple should be constructed with inner_allocator_type(), but that creates an rvalue which cannot bind to inner_allocator_type&, and would also be wrong if this->inner_allocator() != inner_allocator_type(). This could be considered editorial, since the current wording doesn't even compile.

Secondly, in the same paragraph, the tuple objects xprime and yprime seem to be lvalues and might be constructed by copying x and y. This prevents using scoped_allocator to construct pairs from arguments of move-only types. I believe the tuple_cast() expressions should use std::move(x) and std::move(y) to move from the incoming arguments (which are passed by value to candidates for moving) and the final sentence of the paragraph should be:

then calls OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST (*this), p, piecewise_construct, std::move(xprime), std::move(yprime)).

so that the objects are passed to std::pair's piecewise constructor as rvalues and are eligible for moving into the constructor arguments. This could also be considered editorial, as the current wording prevents certain uses which were intended to be supported.

I've implemented these changes and can confirm they allow code to work that can't be compiled according to the current wording.

[2013-03-15 Issues Teleconference]

Moved to Review.

The resolution looks good, with wording provided by a recent implementer. However, it will take more time than the telecon allows to review with confidence, and we would like Pablo to at least take a look over the resolution and confirm that it matches the design intent.

[2013-04-18, Bristol]

Proposed resolution:

This wording is relative to N3376.

  1. Change 20.5.4 [allocator.adaptor.members] paragraph 11 as indicated:

    -11- Effects: Constructs a tuple object xprime from x by the following rules:

    • If uses_allocator<T1, inner_allocator_type>::value is false and is_constructible<T1, Args1...>::value is true, then xprime is x.

    • 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_type()), std::move(x)).

    • 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_type())).

    • Otherwise, the program is ill-formed.

    and constructs a tuple object yprime from y by the following rules:

    • If uses_allocator<T2, inner_allocator_type>::value is false and is_constructible<T2, Args2...>::value is true, then yprime is y.

    • 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_type()), std::move(y)).

    • 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_type())).

    • Otherwise, the program is ill-formed.

    then calls OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST(*this), p, piecewise_construct, std::move(xprime), std::move(yprime)).