As it was the case in Boost, C++11 provides some functions for casting shared_ptr
:
std::static_pointer_cast
std::dynamic_pointer_cast
std::const_pointer_cast
I am wondering, however, why there are no equivalents functions for unique_ptr
.
Consider the following simple example:
class A { virtual ~A(); ... }
class B : public A { ... }
unique_ptr<A> pA(new B(...));
unique_ptr<A> qA = std::move(pA); // This is legal since there is no casting
unique_ptr<B> pB = std::move(pA); // This is not legal
// I would like to do something like:
// (Of course, it is not valid, but that would be the idea)
unique_ptr<B> pB = std::move(std::dynamic_pointer_cast<B>(pA));
Is there any reason why this usage pattern is discouraged, and thus, equivalent functions to the ones present in shared_ptr
are not provided for unique_ptr
?
In addition to Mark Ransom's answer, a
unique_ptr<X, D>
might not even store anX*
.If the deleter defines the type
D::pointer
then that's what is stored, and that might not be a real pointer, it only needs to meet theNullablePointer
requirements and (ifunique_ptr<X,D>::get()
is called) have anoperator*
that returnsX&
, but it isn't required to support casting to other types.unique_ptr
is quite flexible and doesn't necessarily behave very much like a built-in pointer type.As requested, here is an example where the stored type is not a pointer, and therefore casting is not possible. It's a bit contrived, but wraps a made-up database API (defined as a C-style API) in a C++ RAII-style API. The OpaqueDbHandle type meets the
NullablePointer
requirements, but only stores an integer, which is used as a key to lookup the actual DB connection via some implementation-defined mapping. I'm not showing this as an example of great design, just as an example of usingunique_ptr
to manage a non-copyable, movable resource which is not a dynamically-allocated pointer, where the "deleter" doesn't just call a destructor and deallocate memory when theunique_ptr
goes out of scope.I liked cdhowie's answer... but I wanted them to return instead of using out-args. Here's what I came up with:
I put it into a GitHub repo here: https://github.com/friedmud/unique_ptr_cast
The functions you refer to each make a copy of the pointer. Since you can't make a copy of a
unique_ptr
it doesn't make sense to provide those functions for it.To build on Dave's answer, this template function will attempt to move the contents of one
unique_ptr
to another of a different type.Note that the second overload is required for pointers declared
std::unique_ptr<A>
andstd::unique_ptr<B>
. The first function will not work because the first pointer will actually be of typestd::unique_ptr<A, default_delete<A> >
and the second ofstd::unique_ptr<A, default_delete<B> >
; the deleter types won't be compatible and so the compiler will not allow you to use this function.This isn't an answer to why, but it is a way to do it...
It's not entirely clean since for a brief moment 2
unique_ptr
s think they own the same object. And as was commented, you'll also have to manage moving a custom deleter if you use one (but that's very rare).If you're only going the be using the downcast pointer in a small scope, one alternative is to simply downcast the reference to the object being managed by the
unique_ptr
: