Title
Rebinding the allocator before calling construct/destroy in allocate_shared
Status
wp
Section
[util.smartptr.shared.create]
Submitter
Billy O'Neal III

Created on 2019-06-11.00:00:00 last changed 3 weeks ago

Messages

Date: 2024-11-28.21:40:31

Proposed resolution:

This wording is relative to N4988.

  1. Modify [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_for_overwrite(args);
    template<class T, class A, ...>
      shared_ptr<T> allocate_shared_for_overwrite(const A& a, args);
    

    -2- Preconditions: […]

    […]

    -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, pvu, v) or

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

      respectively, where pvu is a pointer of type remove_cv_t<U>* pointsing to storage suitable to hold an object of type remove_cv_t<U> and a2 of type A2 is a potentially rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

    4. (7.6) — […]

    5. (7.7) — When a (sub)object of non-array type U is specified to have a default initial value, allocate_shared shall initializes this (sub)object via the expression allocator_traits<A2>::construct(a2, pvu), where pvu is a pointer of type remove_cv_t<U>* pointsing to storage suitable to hold an object of type remove_cv_t<U> and a2 of type A2 is a potentially rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

    6. […]

    7. [Drafting note: Issue 4024 will add `make_shared_for_overwrite` and `allocate_shared_for_overwrite` to (7.11) but that doesn't conflict with this next edit.]

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

    8. (7.12) — When a (sub)object of 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, pvu) where pvu is a pointer of type remove_cv_t<U>* pointsing to that object of type remove_cv_t<U> and a2 of type A2 is a potentially rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

Date: 2024-11-28.21:40:31

[ Wrocław 2024-11-23; Status changed: Voting → WP. ]

Date: 2024-10-15.00:00:00

[ 2024-10-02; Reflector poll ]

Set status to Tentatively Ready after six votes in favour during reflector poll.

Date: 2024-08-15.00:00:00

[ 2024-08-23; Jonathan provides updated wording ]

`make_shared_default_init` and `allocate_shared_default_init` were renamed by P1973R1 so this needs a rebase. The edit to (7.11) is just for consistency, so that `pv` is always `void*` and `pu` is remove_cv_t<U>*. Accepting this proposed resolution would also resolve issue 3210.

Date: 2024-08-23.10:32:02

[ 2019-07 Issue Prioritization ]

Priority to 3 after discussion on the reflector.

This wording is relative to N4810.

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

    [Drafting note: The edits to change pv to pu were suggested by Jonathan Wakely (thanks!). This wording also has the remove_cv_t fixes specified by LWG 3210 — if that change is rejected some of those have to be stripped here.]

    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, pvu, v) or

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

      respectively, where pvu is a pointer of type remove_cv_t<U>* pointsing to storage suitable to hold an object of type remove_cv_t<U> and a2 of type A2 is a potentially rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

    4. (7.6) — […]

    5. (7.7) — When a (sub)object of non-array type U is specified to have a default initial value, allocate_shared shall initializes this (sub)object via the expression allocator_traits<A2>::construct(a2, pvu), where pvu is a pointer of type remove_cv_t<U>* pointsing to storage suitable to hold an object of type remove_cv_t<U> and a2 of type A2 is a potentially rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

    6. […]

    7. (7.12) — When a (sub)object of 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, pvu) where pvu is a pointer of type remove_cv_t<U>* pointsing to that object of type remove_cv_t<U> and a2 of type A2 is a potentially rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

Date: 2019-06-11.00:00:00

The new allocate_shared wording says we need to rebind the allocator back to T's type before we can call construct or destroy, but this is suboptimal (might make extra unnecessary allocator copies), and is inconsistent with the containers' behavior, which call allocator construct on whatever T they want. (For example, std::list<T, alloc<T>> rebinds to alloc<_ListNode<T>>, but calls construct(T*) without rebinding back)

It seems like we should be consistent with the containers and not require a rebind here. PR would look something like this, relative to N4810; I'm still not super happy with this wording because it looks like it might be saying a copy of the allocator must be made we would like to avoid…

History
Date User Action Args
2024-11-28 21:40:31adminsetmessages: + msg14467
2024-11-28 21:40:31adminsetstatus: voting -> wp
2024-11-19 16:09:07adminsetstatus: ready -> voting
2024-10-02 12:02:45adminsetmessages: + msg14401
2024-10-02 12:02:45adminsetstatus: new -> ready
2024-08-23 10:32:02adminsetmessages: + msg14342
2019-07-23 15:26:26adminsetmessages: + msg10498
2019-06-13 19:52:05adminsetmessages: + msg10438
2019-06-11 00:00:00admincreate