Title
Missing rvalue overloads for shared_ptr operations
Status
c++20
Section
[util.smartptr.shared][util.smartptr.shared.cast]
Submitter
Geoffrey Romer

Created on 2017-07-07.00:00:00 last changed 45 months ago

Messages

Date: 2018-11-12.04:39:29

Proposed resolution:

This wording is relative to N4750.

  1. Edit [memory.syn], header <memory> synopsis, as indicated:

    […]
    // [util.smartptr.shared.cast], shared_ptr casts
    template<class T, class U>
    shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
    shared_ptr<T> static_pointer_cast(shared_ptr<U>&& r) noexcept;
    template<class T, class U>
    shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
    shared_ptr<T> dynamic_pointer_cast(shared_ptr<U>&& r) noexcept;
    template<class T, class U>
    shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
    shared_ptr<T> const_pointer_cast(shared_ptr<U>&& r) noexcept;
    template<class T, class U>
    shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
    shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U>&& r) noexcept;
    […]
    
  2. Edit [util.smartptr.shared], class template shared_ptr synopsis, as indicated:

    template<class T> class shared_ptr {
    public:
      […]
      // [util.smartptr.shared.const], constructors
      […]
      template <class D, class A> shared_ptr(nullptr_t p, D d, A a);
      template<class Y> shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;
      template<class Y> shared_ptr(shared_ptr<Y>&& r, element_type* p) noexcept;
      shared_ptr(const shared_ptr& r) noexcept;
      […]
    };
    […]
    
  3. Edit [util.smartptr.shared.const] as indicated:

    [Drafting note: the use_count() postcondition can safely be deleted because it is redundant with the "shares ownership" wording in the Effects. — end drafting note]

    template<class Y> shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;
    template<class Y> shared_ptr(shared_ptr<Y>&& r, element_type* p) noexcept;
    

    -14- Effects: Constructs a shared_ptr instance that stores p and shares ownership with the initial value of r.

    -15- Postconditions: get() == p && use_count() == r.use_count(). For the second overload, r is empty and r.get() == nullptr.

    -16- [Note: To avoid the possibility of a dangling pointer, the user of this constructor must ensure that p remains valid at least until the ownership group of r is destroyed. — end note]

    -17- [Note: This constructor allows creation of an empty shared_ptr instance with a non-null stored pointer. — end note]

  4. Edit [util.smartptr.shared.cast] as indicated:

    template<class T, class U>
      shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
      shared_ptr<T> static_pointer_cast(shared_ptr<U>&& r) noexcept;
    

    -1- Requires: The expression static_cast<T*>((U*)nullptr) shall be well-formed.

    -2- Returns:

    shared_ptr<T>(rR, static_cast<typename shared_ptr<T>::element_type*>(r.get()))
    , where R is r for the first overload, and std::move(r) for the second.

    -3- [Note: The seemingly equivalent expression shared_ptr<T>(static_cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice. — end note]

    template<class T, class U>
      shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
      shared_ptr<T> dynamic_pointer_cast(shared_ptr<U>&& r) noexcept;
    

    -4- Requires: The expression dynamic_cast<T*>((U*)nullptr) shall be well-formed. The expression dynamic_cast<typename shared_ptr<T>::element_type*>(r.get()) shall be well formed and shall have well-defined behavior.

    -5- Returns:

    1. (5.1) — When dynamic_cast<typename shared_ptr<T>::element_type*>(r.get()) returns a non-null value p, shared_ptr<T>(rR, p), where R is r for the first overload, and std::move(r) for the second.

    2. (5.2) — Otherwise, shared_ptr<T>().

    -6- [Note: The seemingly equivalent expression shared_ptr<T>(dynamic_cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice. — end note]

    template<class T, class U>
      shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
      shared_ptr<T> const_pointer_cast(shared_ptr<U>&& r) noexcept;
    

    -7- Requires: The expression const_cast<T*>((U*)nullptr) shall be well-formed.

    -8- Returns:

    shared_ptr<T>(rR, const_cast<typename shared_ptr<T>::element_type*>(r.get()))
    , where R is r for the first overload, and std::move(r) for the second.

    -9- [Note: The seemingly equivalent expression shared_ptr<T>(const_cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice. — end note]

    template<class T, class U>
      shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U>& r) noexcept;
    template<class T, class U>
      shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U>&& r) noexcept;
    

    -10- Requires: The expression reinterpret_cast<T*>((U*)nullptr) shall be well-formed.

    -11- Returns:

    shared_ptr<T>(rR, reinterpret_cast<typename shared_ptr<T>::element_type*>(r.get()))
    , where R is r for the first overload, and std::move(r) for the second.

    -12- [Note: The seemingly equivalent expression shared_ptr<T>(reinterpret_cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice. — end note]

Date: 2018-11-12.04:39:29

[ 2018-11, Adopted in San Diego ]

Date: 2018-06-04.19:31:30

[ 2018-06 Rapperswil Monday AM ]

Move to Ready; choosing the PR in the issue as opposed to P0390R0 and rebase wording to most recent working draft

Date: 2017-07-12.01:58:24

[ 2017-07 Toronto Tuesday PM issue prioritization ]

Status LEWG

Date: 2017-07-10.16:31:32

The shared_ptr aliasing constructor and the shared_ptr casts are specified to take a shared_ptr by const reference and construct a new shared_ptr that shares ownership with it, and yet they have no corresponding rvalue reference overloads. That results in an unnecessary refcount increment/decrement when those operations are given an rvalue. Rvalue overloads can't be added as a conforming extension because they observably change semantics (but mostly only for code that does unreasonable things like pass an argument by move and then rely on the fact that it's unchanged), and [res.on.arguments]/p1.3 doesn't help because it only applies to rvalue reference parameters.

This issue is related to P0390R0.

History
Date User Action Args
2021-02-25 10:48:01adminsetstatus: wp -> c++20
2018-11-12 04:39:29adminsetmessages: + msg10185
2018-11-12 04:39:29adminsetstatus: voting -> wp
2018-10-08 05:13:59adminsetstatus: ready -> voting
2018-06-04 18:30:10adminsetmessages: + msg9861
2018-06-04 18:30:10adminsetstatus: lewg -> ready
2017-07-12 01:58:24adminsetmessages: + msg9359
2017-07-12 01:58:24adminsetstatus: new -> lewg
2017-07-08 12:58:20adminsetmessages: + msg9324
2017-07-07 00:00:00admincreate