§[allocator.uses.construction] functions misbehave for const types
Jonathan Wakely

Created on 2019-02-28.00:00:00, last changed 2019-03-03.22:12:53.


Date: 2019-03-03.22:12:53

Proposed resolution:

This wording is relative to N4800.

  1. Change [allocator.uses.construction] as indicated:

    template <class T, class Alloc, class... Args>
      auto uses_allocator_construction_args(const Alloc& alloc, Args&&... args) -> see below;

    -4- Constraints: T is not a specialization of pair.

    -5- Returns: A tuple value determined as follows, where U denotes the type remove_cv_t<T>:

    1. (5.1) — If uses_allocator_v<TU, Alloc> is false and is_constructible_v<T, Args...> is true, return forward_as_tuple(std::forward<Args>(args)...).

    2. (5.2) — Otherwise, if uses_allocator_v<TU, Alloc> is true and is_constructible_v<T, allocator_arg_t, Alloc, Args...> is true, return

      tuple<allocator_arg_t, const Alloc&, Args&&...>(
        allocator_arg, alloc, std::forward<Args>(args)...)
    3. (5.3) — Otherwise, if uses_allocator_v<TU, Alloc> is true and is_constructible_v<T, Args..., Alloc> is true, return forward_as_tuple(std::forward<Args>(args)..., alloc).

    4. (5.4) — Otherwise, the program is ill-formed.

    template <class T, class Alloc, class Tuple1, class Tuple2>
      auto uses_allocator_construction_args(const Alloc& alloc, piecewise_construct_t,
                                            Tuple1&& x, Tuple2&& y) -> see below;

    -6- Constraints: T is a specialization of pair.

    -7- Effects: For T specified as (possibly const) pair<T1, T2>, equivalent to:


    template <class T, class Alloc, class... Args>
      T* uninitialized_construct_using_allocator(T* p, const Alloc& alloc, Args&&... args);

    -17- Effects: Equivalent to:

    return ::new(static_cast<void*>voidify(*p))
      T(make_obj_using_allocator<T>(alloc, std::forward<Args>(args)...));
Date: 2019-02-28.00:00:00

The new functions added by P0591R4 misbehave for cv-qualified types. A specialization std::uses_allocator<X, Alloc> will not match const X, so std::uses_allocator_construction_args<const X> will return a different result from std::uses_allocator_construction_args<X>. It makes no sense to construct X and const X differently, either the type wants to use an allocator or it doesn't. I think std::uses_allocator_construction_args<T> should remove cv-qualifiers before checking uses_allocator, so that it works consistently.

We could consider changing std::make_obj_using_allocator to also strip cv-qualifiers, but it's not necessary as C++17 guaranteed elision works even for prvalues of const types. We only need to make the construction args ignore cv-qualifiers. We don't want to make cv-qualified types ill-formed, because that would require users of uses-allocator construction to strip cv-qualifiers before using these functions, e.g. in cases like std::tuple<const int> t(allocator_arg, alloc, 1);

Date User Action Args
2019-03-03 22:12:53adminsetmessages: + msg10338
2019-02-28 00:00:00admincreate