This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3210. `allocate_shared` is inconsistent about removing `const` from the pointer passed to allocator `construct` and `destroy`

Section: 20.3.2.2.7 [util.smartptr.shared.create] Status: New Submitter: Billy O'Neal III Opened: 2019-05-29 Last modified: 2020-09-06

Priority: 3

View other active issues in [util.smartptr.shared.create].

View all other issues in [util.smartptr.shared.create].

View all issues with New status.

Discussion:

I implemented the fix for LWG 3008 and Stephan pointed out there's an inconsistency here for `allocate_shared<const T>`.

20.3.2.2.7 [util.smartptr.shared.create] p3 says that the allocator construct call is done without removing cv qualifiers, but 20.3.2.2.7 [util.smartptr.shared.create] p7.12 says that the `destroy` call is done with removed cv qualifiers.

The fallback for `allocator_traits::construct` rejects `const T*` (since it `static_casts` to `void*`), so the most likely outcome of attempting to do this today is to fail to compile, which is a break with C++17.

Our options are:

1. Fix the allocator model to deal with `const` elements somehow, which breaks compatibility with existing allocators unprepared for `const` elements here. We would need to extend the allocator requirements to allow `const T*` to be passed here, and fix our default to `const_cast`.

2. Fix `allocate_shared` to remove `const` before calling `construct`, which changes the experience for C++17 customers because `allocate_shared` constructs a `T` instead of a `const T`, but not in a way substantially different to edits P0674 already made here.

3. Back out `allocate_shared`'s interaction with this part of the allocator model (reverting this part of P0674 and reopening LWG 3008).

4. Go around the problem by prohibiting `allocate_shared<const T>`, which breaks existing C++17 customers.

Billy O'Neal argues that only (2) preserves the design intent P0674 while maintaining compatibility for most allocators and most C++17 customers.

Peter Dimov argues that (1) isn't likely to break enough to matter.

[2019-06-16 Priority set to 3 based on reflector discussion]

Proposed resolution:

This wording is relative to N4810.

> [Drafting note: As the issue submitter prefers option (2), this is wording for that.]

1. Modify 20.3.2.2.7 [util.smartptr.shared.create] as indicated:

   >     template<class T, ...>
   >       shared_ptr<T> make_shared(args);
   >     template<class T, class A, ...>
   >       shared_ptr<T> allocate_shared(const A& a, args);
   >     template<class T, ...>
   >       shared_ptr<T> make_shared_default_init(args);
   >     template<class T, class A, ...>
   >       shared_ptr<T> allocate_shared_default_init(const A& a, args);

   > > -2- Requires: […]

   > > […]

   > > -7- Remarks:

   > > 1. (7.1) — […]

   > > 2. […]

   > > 3. (7.5) — When a (sub)object of a non-array type `U` is specified to have an initial value of `v`, or `U(l...)`, where `l...` is a list of constructor arguments, `allocate_shared` shall initialize this (sub)object via the expression

   > >    1. (7.5.1) — `allocator_traits<A2>::construct(a2, pv, v)` or

   > >    2. (7.5.2) — `allocator_traits<A2>::construct(a2, pv, l...)`

   > >    respectively, where `pv` points to storage suitable to hold an object of type `remove_cv_t<U>` and `a2` of type `A2` is a rebound copy of the allocator `a` passed to `allocate_shared` such that its `value_type` is `remove_cv_t<U>`.