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

### 3008. `make_shared` (sub)object destruction semantics are not specified

Section: 20.3.2.2.7 [util.smartptr.shared.create] Status: C++20 Submitter: Glen Joseph Fernandes Opened: 2017-08-06 Last modified: 2021-02-25

Priority: 2

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

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

View all issues with C++20 status.

Discussion:

The remarks for the `make_shared` and `allocate_shared` functions do not specify how the objects managed by the returned `shared_ptr` are destroyed. It is implied that when objects are constructed via a placement new expression, they are destroyed by calling the destructor, and that when objects are constructed via an allocator, they are destroyed using that allocator. This should be explicitly specified.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 2

Previous resolution [SUPERSEDED]:

> This resolution is relative to N4687.

> 1. Edit 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);

>    > > […]

>    > > -7- Remarks:

>    > > 1. […]

>    > > 2. (7.9) — When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements should be destroyed in the reverse order of their construction.

>    > > 3. (7.?) — When a (sub)object of a non-array type `U` that was initialized by `make_shared` is to be destroyed, it shall be destroyed via the expression `pv->~U()` where `pv` points to that object of type `U`.

>    > > 4. (7.?) — When a (sub)object of a non-array type `U` that was initialized by `allocate_shared` is to be destroyed, it shall be destroyed via the expression `allocator_traits<A2>::destroy(a2, pv)` where `pv` points to that object of type cv-unqualified `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>`.

[2018-06 Rapperswil Wednesday night issues processing]

CC: what is "of type cv-unqualified `U`" and "`remove_cv_T<U>`" about?
DK: again, it isn't new wording; it is in p 7.5.2
JW: but none of the words use "of type cv-unqualified `U`"
CT: so we should also used `remove_cv_T<U>` instead?
JW: I would like to talk to Glen
FB: does anybody know how it works for an array of arrays? It seems to cover the case
JW: we could leave it vague as it is now or specify it to exactly what it does
DK: I think we should split the thing into two parts and start with definitions
DK: ACTION I can refactor the wording
MC: there was a fairly long message thread when we talked about this

Daniel comments and improves wording:

The currently allocator requirements support only the construction of cv-unqualified object types (See Table 30 type `C` and pointer variable `c` as well as Table 31 expressions "`a.construct(c, args)`" and "`a.destroy(c)`"), therefore a conforming implementation needs to effectively construct an object pointer that holds an object of type `remove_cv_T<U>` and similarly destroy such an object. Albeit it seems to be an artificial restriction to construct and destroy only non-cv-qualified object types, this is, if any, a different issue. But given this current state, the wording for `allocate_shared` needs to make a special wording dance via `remove_cv_T<U>`. For `construct` the existing wording prevents to speak about that detail by using the more indirect phrase "where `pv` points to storage suitable to hold an object of type `U`", but since object types `U` and `const U` have exactly the same storage and alignment requirements, this sentence is correct for `remove_cv_T<U>` as well.

[2018-08-23 Batavia Issues processing]

Status to Tentatively Ready.

[2018-11, Adopted in San Diego]

Proposed resolution:

This resolution is relative to N4750.

1. Edit 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);

   > > […]

   > > -7- Remarks:

   > > 1. […]

   > > 2. (7.9) — When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements are destroyed in the reverse order of their original construction.

   > > 3. (7.?) — When a (sub)object of a non-array type `U` that was initialized by `make_shared` is to be destroyed, it is destroyed via the expression `pv->~U()` where `pv` points to that object of type `U`.

   > > 4. (7.?) — When a (sub)object of a non-array type `U` that was initialized by `allocate_shared` is to be destroyed, it is destroyed via the expression `allocator_traits<A2>::destroy(a2, pv)` where `pv` points to that 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>`.