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>.