| Document number | P0497R0 |
| Date | 2016-11-10 |
| Project | Programming Language C++, Library Working Group |
| Reply-to | Jonathan Wakely <cxx@kayari.org> |
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.
shared_ptr construction from unique_ptrOriginally 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>::pointeris convertible toT*.
I combined these in P0414R1 as:
Remark: This constructor shall not participate in overload resolution unless
unique_ptr<Y, D>::pointerisconvertible 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>::pointeris convertible toelement_type*.
The "compatible with" check prevents undesirable conversions from
unique_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().
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]> comparisonsThe 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()), whereVis 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*.
enable_shared_from_thisIt 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.
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_ptrobject that owns the pointerp. WhenTis not an array type, eEnablesshared_from_thiswithp. If an exception is thrown,delete pis called.[...]
-8- Requires: [...]
-9- Effects: Constructs a
shared_ptrobject that owns the objectpand the deleterd. WhenTis not an array type, tThe first and second constructors enableshared_from_thiswithp. The second and fourth constructors shall use a copy ofato 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>::pointerY*is compatible withT*andunique_ptr<Y, D>::pointeris convertible toelement_type*.-27 Effects: If
r.get() == nullptr, equivalent toshared_ptr(). Otherwise, ifDis 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. WhenTis not an array typeIf, enablesr.get() != nullptrshared_from_thiswith 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()), whereVis 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*.
Thanks to Peter Dimov for reviewing these suggestions.