C++ Logo

std-proposals

Advanced search

Re: pointer_cast for unique_ptr

From: Barry Revzin <barry.revzin_at_[hidden]>
Date: Sun, 27 Dec 2020 22:06:13 -0600
On Sun, Dec 27, 2020 at 9:16 PM connor horman via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> This came up in some code I'm helping with, but I noticed that there are
> not overloads of *_pointer_cast for unique_ptr. This seems like a potential
> failing in the spec, so I propose to add these overloads, and the machinery
> to do so.


"potential failing in the spec" isn't really very motivating. What is the
problem that needs these overloads, and why?


>
> To std::default_deleter<T>, add a member template<typename U> rebind =
> default_deleter<U>;
> Add a similar member to the default_deleter<T[]>.
> Additionally, modify the constructor of default_deleter to participate in
> overload resolution if any of the following is true:
> - U is a (possibly virtual) base class of T (does this need to allow
> inaccessible base classes, or is that not permitted by an implicit
> conversion),
> - T is an accessible (possibly virtual) base class of U, in a context
> unrelated to both T and U,
> - U is (possibly-differently cv-qualified) T, or
> - Both T and U are polymorphic types and U has a virtual destructor.
> Such that the constructor is explicit iff T* is implicitly convertible to
> U*
> Likewise, permit any qualification conversion in the constructor of the
> T[] specialization (constructor is explicit if U(*)[] is less qualified
> then T(*)[])
>
> The, add all of the following overloads to the <memory> header, as follows:
>
> Let REBIND_DELETER be an exposition-only template alias, where,
> REBIND_DELETER<D,U> is typename D::template rebind<U> if that type is
> well-formed, otherwise, if D is Deleter<T,Rest...> for some template
> Deleter and some list of types Rest..., REBIND_DELETER<D,U> is
> Deleter<U,Rest...>, otherwise, the type is not well-formed.
>
> template<typename U,typename T,typename D>
> std::unique_ptr<U,REBIND_DELETER<D,U>>
> const_pointer_cast(std::unique_ptr<T,D>&& ptr)
> - Performs a type qualification conversion of a unique_ptr, as though by
> const_cast
> - Only participates in overload resolution if REBIND_DELETER<D,U> is well
> formed and is constructible from D,
> typename std::pointer_traits<typename D::pointer>::template rebind<U> is
> implicitly convertible to typename REBIND_DELETER<D,U>::pointer, and the
> conversion from T* to U* may be performed by a valid const_cast
> - After this call, ptr does not own an object.
>
> template<typename U,typename T,typename D>
> std::unique_ptr<U,REBIND_DELETER<D,U>>
> staitic_pointer_cast(std::unique_ptr<T,D>&& ptr)
> - Performs a type qualification conversion of a unique_ptr, as though by
> const_cast
> - Only participates in overload resolution if REBIND_DELETER<D,U> is well
> formed and constructible from D,
> typename std::pointer_traits<typename D::pointer>::template rebind<U> is
> implicitly convertible to typename REBIND_DELETER<D,U>::pointer, and the
> conversion from T* to U* may be performed by a valid static_cast
> - After this call, ptr does not own an object.
>
> template<typename U,typename T,typename D>
> std::unique_ptr<U,REBIND_DELETER<D,U>>
> dynamic_pointer_cast(std::unique_ptr<T,D>&& ptr)
> - Performs a type qualification conversion of a unique_ptr, as though by
> const_cast
> - Only participates in overload resolution if REBIND_DELETER<D,U> is well
> formed and constructible from D,
> typename std::pointer_traits<typename D::pointer>::template rebind<U> is
> implicitly convertible to typename REBIND_DELETER<D,U>::pointer, the
> conversion from T* to U* may be performed by a valid dynamic_cast
> - If the dynamic_cast succeeds, ptr will not own an object. Otherwise, ptr
> is left in an unmodified state, and the result does not own an object.
>

In the specific case of unique_ptr<T, default_delete<T>>, it is possible to
soundly implement all of these functions. There is also prior art in Boost (
https://www.boost.org/doc/libs/1_75_0/libs/smart_ptr/doc/html/smart_ptr.html#pointer_cast),
again specifically and exclusively for the default deleter case.

But in the general case of unique_ptr<T, D>, it seems pretty unlikely that
you could reliably rebind the deleter in a way that remains valid. Do you
have any examples of such? On the plus side, for those deleters that today
happen to be of the form C<T, Args...>, it seems unlikely that they'd just
happen to be convertible to C<U, Args...>, so I'd expect this to just never
work at all (as opposed to the worst case of compiling but having an
improper deleter.).

Barry

Received on 2020-12-27 22:06:28