Fixes to shared_ptr support for arrays (original) (raw)
Document number | P0497R0 |
---|---|
Date | 2016-11-10 |
Project | Programming Language C++, Library Working Group |
Reply-to | Jonathan Wakely cxx@kayari.org |
Introduction
LWG Motion 6 in Jacksonville was to apply to the C++ working paper the wording from P0220R1,Adopt Library Fundamentals V2 TS Components for C++17 (R1), but due to conflicts with changes in the C++ WP the shared_ptr
changes were not applied. P0414R1 remedied that, by clarifying how to apply the changes from the TS to the current draft.
That does not entirely resolve the matter, because the specification in the TS is missing some things (either by accident, or because the specification in the WP changed since a snapshot of it was taken for the TS). There are four issues with shared_ptr
array support which need to be addressed after P0414R1 is applied to the WP.
Incorrect constraint for shared_ptr
construction from unique_ptr
Originally this constructor was unconstrained and had no requirements. The TS has a requirement added by [N3290][n3290]:
Requires:
Y*
shall be compatible withT*
.
In parallel, LWG 2399 added a slightly different constraint to the WP:
Remark: This constructor shall not participate in overload resolution unless
unique_ptr<Y, D>::pointer
is convertible toT*
.
I combined these in P0414R1 as:
Remark: This constructor shall not participate in overload resolution unless
unique_ptr<Y, D>::pointer
isconvertible tocompatible withT*
.
Based on implementation experience I believe the correct form is:
Remark: This constructor shall not participate in overload resolution unless
Y*
is compatible withT*
andunique_ptr<Y, D>::pointer
is convertible toelement_type*
.
The "compatible with" check prevents undesirable conversions fromunique_ptr<T[]>
to shared_ptr<T>
and the "convertible to" check ensures that the result of unique_ptr<Y, D>::get()
can be stored in the shared_ptr
and returned by shared_ptr<T>::get()
.
Missing constraint for weak_ptr
construction from weak_ptr
rvalues.
LWG 2315 added new weak_ptr
constructors to the WP which are not present in the TS and so were not adjusted for array support. The fix here is to simply apply the same constraints as for the other weak_ptr
constructors, requiring that Y*
is compatible with T*
.
shared_ptr<T[]>
and shared_ptr<T[N]>
comparisons
The definition of operator<(const shared_ptr<T>& a, const shared_ptr<U>&)
in [util.smartptr.shared.cmp] p2 says:
Returns:
less<V>()(a.get(), b.get())
, whereV
is the composite pointer type (Clause 5) ofT*
andU*
.
There is no common type for types such as A(*)[2]
and B(*)[1]
, so mixed comparisons are not possible.
The definition of operator<(const shared_ptr<T>& a, nullptr_t)
in [util.smartptr.shared.cmp] p6 says:
Returns: The first function template returns
less<T*>()(a.get(), nullptr)
. The second function template returnsless<T*>()(nullptr, a.get())
.
When T
is an array type a.get()
is not convertible to T*
, so it fails to compile.
Both functions should use less<element_type*>
instead of simply T*
.
Interaction with enable_shared_from_this
It doesn't make sense to treat the first element of an array specially, so a shared_ptr
to an array should not enable shared_from_this
on construction.
Proposed Wording
Changes are relative to N4606.
Change 20.11.2.2.1 shared_ptr
constructors [util.smartptr.shared.const]:
template<class Y> explicit shared_ptr(Y* p);
-4- Requires: [...]
-5-Effects: Constructs a
shared_ptr
object that owns the pointerp
. WhenT
is not an array type, eEnablesshared_from_this
withp
. If an exception is thrown,delete p
is called.[...]
-8- Requires: [...]
-9- Effects: Constructs a
shared_ptr
object that owns the objectp
and the deleterd
. WhenT
is not an array type, tThe first and second constructors enableshared_from_this
withp
. The second and fourth constructors shall use a copy ofa
to allocate memory for internal use. If an exception is thrown,d(p)
is called.[...]
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
-26-Remark: This constructor shall not participate in overload resolution unless
unique_ptr<Y, D>::pointer
Y*
is compatible withT*
andunique_ptr<Y, D>::pointer
is convertible toelement_type*
.-27 _Effects:_If
r.get() == nullptr
, equivalent toshared_ptr()
. Otherwise, ifD
is not a reference type, equivalent toshared_ptr(r.release(), r.get_deleter())
. Otherwise, equivalent toshared_ptr(r.release(), ref(r.get_deleter()))
. If an exception is thrown, the constructor has no effect.WhenT
is not an array typeIf, enablesr.get() != nullptr
shared_from_this
with the value that was returned byr.release()
.
Change 20.11.2.2.7 shared_ptr
comparison [util.smartptr.shared.cmp]:
template<class T, class U> bool operator<(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
-2-Returns:
less<V>()(a.get(), b.get())
, whereV
is the composite pointer type (Clause 5) ofshared_ptr<T>::element_type*
andshared_ptr<U>::element_type*
.[...]
template <class T> bool operator<(const shared_ptr<T>& a, nullptr_t) noexcept; template <class T> bool operator<(nullptr_t, const shared_ptr<T>& a) noexcept;
-6-Returns: The first function template returns
less<shared_ptr<T>::element_type*>()(a.get(), nullptr)
. The second function template returnsless<shared_ptr<T>::element_type*>()(nullptr, a.get())
.
Change 20.11.2.3.1 weak_ptr
constructors [util.smartptr.weak.const]:
weak_ptr(weak_ptr&& r) noexcept; template<class Y> weak_ptr(weak_ptr<Y>&& r) noexcept;
-3-Remark: The second constructor shall not participate in overload resolution unless
Y*
isimplicitly convertible tocompatible withT*
.
Acknowledgments
Thanks to Peter Dimov for reviewing these suggestions.