I have a series of factories that return unique_ptr<Base>
. Under the hood, though, they are providing pointers to various derived types, i.e unique_ptr<Derived>
, unique_ptr<DerivedA>
, unique_ptr<DerivedB>
etc.
Given DerivedA : Derived
and Derived : Base
we'd have:
unique_ptr<Base> DerivedAFactory() {
return unique_ptr<Base>(new DerivedA);
}
What I need to do is to "cast" the pointer from the returned unique_ptr<Base>
to some derived level (not necessarily the original internal one). To illustrate in pseudo code:
unique_ptr<Derived> ptr = static_cast<unique_ptr<Derived>>(DerivedAFactory());
I'm thinking of doing this by releasing the object from the unique_ptr
, then using a function that casts the raw pointer and reassigns that to another unique_ptr
of the desired flavor (the release
would be explicitly done by the caller prior to the call):
unique_ptr<Derived> CastToDerived(Base* obj) {
return unique_ptr<Derived>(static_cast<Derived*>(obj));
}
Is this valid, or is / will there be something funky going on?
PS. There is an added complication in that some of the factories reside in DLLs that are dynamically loaded at run-time, which means I need to make sure the produced objects are destroyed in the same context (heap space) as they were created. The transfer of ownership (which typically happens in another context) must then supply a deleter from the original context. But aside from having to supply / cast a deleter along with the pointer, the casting problem should be the same.
I'd create a couple of function templates, static_unique_ptr_cast
and dynamic_unique_ptr_cast
. Use the former in cases where you're absolutely certain the pointer is actually a Derived *
, otherwise use the latter.
template<typename Derived, typename Base, typename Del>
std::unique_ptr<Derived, Del>
static_unique_ptr_cast( std::unique_ptr<Base, Del>&& p )
{
auto d = static_cast<Derived *>(p.release());
return std::unique_ptr<Derived, Del>(d, std::move(p.get_deleter()));
}
template<typename Derived, typename Base, typename Del>
std::unique_ptr<Derived, Del>
dynamic_unique_ptr_cast( std::unique_ptr<Base, Del>&& p )
{
if(Derived *result = dynamic_cast<Derived *>(p.get())) {
p.release();
return std::unique_ptr<Derived, Del>(result, std::move(p.get_deleter()));
}
return std::unique_ptr<Derived, Del>(nullptr, p.get_deleter());
}
The functions are taking an rvalue reference to ensure that you're not pulling the rug out from underneath the caller's feet by stealing the unique_ptr
passed to you.