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.

### 2996. Missing rvalue overloads for `shared_ptr` operations

Section: 20.11.3 [util.smartptr.shared], 20.11.3.10 [util.smartptr.shared.cast] Status: C++20 Submitter: Geoffrey Romer Opened: 2017-07-07 Last modified: 2021-02-25

Priority: Not Prioritized

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

View all issues with C++20 status.

Discussion:

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.

[2017-07 Toronto Tuesday PM issue prioritization]

Status LEWG

[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

[2018-11, Adopted in San Diego]

Proposed resolution:

This wording is relative to N4750.

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

   >     […]
   >     // 20.11.3.10 [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 20.11.3 [util.smartptr.shared], class template `shared_ptr` synopsis, as indicated:

   >     template<class T> class shared_ptr {
   >     public:
   >       […]
   >       // 20.11.3.2 [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 20.11.3.2 [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 20.11.3.10 [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]