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.

### 2782. `scoped_allocator_adaptor` constructors must be constrained

Section: 20.13.3 [allocator.adaptor.cnstr] Status: C++17 Submitter: Jonathan Wakely Opened: 2016-10-14 Last modified: 2017-07-30

Priority: 0

View all issues with C++17 status.

Discussion:

The templated constructors of `scoped_allocator_adaptor` need to be constrained, otherwise uses-allocator construction gives the wrong answer and causes errors for code that should compile.

Consider two incompatible allocator types:

>     template<class T> struct Alloc1 { ... };
>     template<class T> struct Alloc2 { ... };
>     static_assert(!is_convertible_v<Alloc1<int>, Alloc2<int>>);

The unconstrained constructors give this bogus answer:

>     template<class T> using scoped = scoped_allocator_adaptor<T>;
>     static_assert(is_convertible_v<scoped<Alloc1<int>>, scoped<Alloc2<int>>>);

This causes `uses_allocator` to give the wrong answer for any specialization involving incompatible `scoped_allocator_adaptors`, which makes `scoped_allocator_adaptor::construct()` take an ill-formed branch e.g.

>     struct X
>     {
>       using allocator_type = scoped<Alloc2<int>>;
>       X(const allocator_type&);
>       X();
>     };
>     scoped<Alloc1<int>>{}.construct((X*)0);

This fails to compile, because `uses_allocator<X, scoped_allocator_adaptor<Alloc2<int>>>` is `true`, so the allocator is passed to the `X` constructor, but the conversion fails. The error is outside the immediate context, and so is a hard error.

[2016-11-12, Issaquah]

Sat AM: Priority 0; move to Ready

Billy to open another issue about the confusion with the ctor

Proposed resolution:

This wording is relative to N4606.

1. Modify 20.13.3 [allocator.adaptor.cnstr] by converting "Requires" elements to "Remarks: shall not participate ..." constraints as shown:

   >     template <class OuterA2>
   >       scoped_allocator_adaptor(OuterA2&& outerAlloc,
   >                                const InnerAllocs&... innerAllocs) noexcept;

   > > -2- Requires: `OuterAlloc` shall be constructible from `OuterA2`.

   > > -3- Effects: Initializes the `OuterAlloc` base class with `std::forward<OuterA2>(outerAlloc)` and inner with `innerAllocs...` (hence recursively initializing each allocator within the adaptor with the corresponding allocator from the argument list).

   > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<OuterAlloc, OuterA2>` is `true`.

   > > […]

   >     template <class OuterA2>
   >       scoped_allocator_adaptor(const scoped_allocator_adaptor<OuterA2,
   >                                InnerAllocs...>& other) noexcept;

   > > -6- Requires: `OuterAlloc` shall be constructible from `OuterA2`.

   > > -7- Effects: Initializes each allocator within the adaptor with the corresponding allocator from `other`.

   > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<OuterAlloc, const OuterA2&>` is `true`.

   >     template <class OuterA2>
   >       scoped_allocator_adaptor(scoped_allocator_adaptor<OuterA2,
   >                                InnerAllocs...>&& other) noexcept;

   > > -8- Requires: `OuterAlloc` shall be constructible from `OuterA2`.

   > > -9- Effects: Initializes each allocator within the adaptor with the corresponding allocator rvalue from `other`.

   > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<OuterAlloc, OuterA2>` is `true`.