shared_ptr
operationsSection: 20.3.2.2 [util.smartptr.shared], 20.3.2.2.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.
[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.
Edit 20.2.2 [memory.syn], header <memory>
synopsis, as indicated:
[…] // 20.3.2.2.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; […]
Edit 20.3.2.2 [util.smartptr.shared], class template shared_ptr
synopsis, as indicated:
template<class T> class shared_ptr { public: […] // 20.3.2.2.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; […] }; […]
Edit 20.3.2.2.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
-15- Postconditions:shared_ptr
instance that storesp
and shares ownership with the initial value ofr
.get() == p
. For the second overload,&& use_count() == r.use_count()r
is empty andr.get() == nullptr
. -16- [Note: To avoid the possibility of a dangling pointer, the user of this constructor must ensure thatp
remains valid at least until the ownership group ofr
is destroyed. — end note] -17- [Note: This constructor allows creation of an emptyshared_ptr
instance with a non-null stored pointer. — end note]
Edit 20.3.2.2.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
-2- Returns:static_cast<T*>((U*)nullptr)
shall be well-formed., whereshared_ptr<T>(
rR, static_cast<typename shared_ptr<T>::element_type*>(r.get()))R
isr
for the first overload, andstd::move(r)
for the second. -3- [Note: The seemingly equivalent expressionshared_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
-5- Returns:dynamic_cast<T*>((U*)nullptr)
shall be well-formed. The expressiondynamic_cast<typename shared_ptr<T>::element_type*>(r.get())
shall be well formed and shall have well-defined behavior.
(5.1) — When
dynamic_cast<typename shared_ptr<T>::element_type*>(r.get())
returns a non-null valuep
,shared_ptr<T>(
, whererR, p)R
isr
for the first overload, andstd::move(r)
for the second.(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
-8- Returns:const_cast<T*>((U*)nullptr)
shall be well-formed., whereshared_ptr<T>(
rR, const_cast<typename shared_ptr<T>::element_type*>(r.get()))R
isr
for the first overload, andstd::move(r)
for the second. -9- [Note: The seemingly equivalent expressionshared_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
-11- Returns:reinterpret_cast<T*>((U*)nullptr)
shall be well-formed., whereshared_ptr<T>(
rR, reinterpret_cast<typename shared_ptr<T>::element_type*>(r.get()))R
isr
for the first overload, andstd::move(r)
for the second. -12- [Note: The seemingly equivalent expressionshared_ptr<T>(reinterpret_cast<T*>(r.get()))
will eventually result in undefined behavior, attempting to delete the same object twice. — end note]