Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Downcasting" unique_ptr<Base> to unique_ptr<Derived>

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.

like image 735
d7samurai Avatar asked Jan 16 '14 22:01

d7samurai


People also ask

Can a unique_ptr be copied?

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.

What is unique_ptr?

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.

Can you move a unique_ptr?

A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.

How does C++ unique_ptr work?

A unique_ptr object wraps around a raw pointer and its responsible for its lifetime. When this object is destructed then in its destructor it deletes the associated raw pointer. unique_ptr has its -> and * operator overloaded, so it can be used similar to normal pointer.


1 Answers

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.

like image 53
Praetorian Avatar answered Sep 24 '22 02:09

Praetorian