How to perform a dynamic_cast with a unique_ptr?

后端 未结 4 869
忘了有多久
忘了有多久 2020-12-15 16:31

I have a class hierarchy as follows:

class BaseSession : public boost::enable_shared_from_this
class DerivedSessionA : public BaseSession
         


        
相关标签:
4条回答
  • 2020-12-15 17:13

    Simply get the stored pointer using the std::unique_ptr<>::get() method:

    Func(dynamic_cast<DerivedSessionA*>(shared_from_this().get()))
    

    that if shared_from_this() has that prototype:

    std::unique_ptr<BaseSession>& shared_from_this();
    
    0 讨论(0)
  • 2020-12-15 17:32

    Unless you want to transfer ownership of your std::unique_ptr<T>, your function should take pointer or reference to T.

    So signature of Func should be something like Func(DerivedSessionA*)

    and then your call may look like:

    std::unique_ptr<BaseSession> ptr; // Initialize it with correct value
    
    Func(dynamic_cast<DerivedSessionA*>(ptr.get()));
    

    Or as you seems to call it directly from a method in BaseSession:

    Func(dynamic_cast<DerivedSessionA*>(this));
    
    0 讨论(0)
  • 2020-12-15 17:37

    Update

    The question has been clarified:

    sorry I was not clear. I want the ownership to remain with original owner, the called function should only get reference to it, not ownership. Not looking for two smart pointer for the same object.

    In that case, the solution is simply:

    dynamic_cast<B&>(*my_unique_ptr)
    

    Done. It throws if the cast doesn't succeed.


    Casting shared_ptr

    For shared_ptr there is std::dynamic_pointer_cast<> (http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast)

    Casting unique_ptr

    The simplest way would seem:

    #include <memory>
    
    struct A { virtual ~A() = default; };
    struct B : A { };
    
    int main()
    {
        std::unique_ptr<A> pa(new B);
    
        std::unique_ptr<B> pb(dynamic_cast<B*>(pa.release())); // DO NOT DO THIS
    }
    

    As the commenter rightfully points out, this may leak the object if the conversion failed. That's not very helpful.

    A reason why the dynamic_unique_ptr_cast<> doesn't exist might be that the unique_ptr type doesn't erase the deleter. It could be hard/impossible to choose an appropriate delete for the target pointer type.

    However, for simple cases, you could use something like this:

    template <typename To, typename From, typename Deleter> 
        std::unique_ptr<To, Deleter> dynamic_unique_cast(std::unique_ptr<From, Deleter>&& p) {
            if (To* cast = dynamic_cast<To*>(p.get()))
            {
                std::unique_ptr<To, Deleter> result(cast, std::move(p.get_deleter()));
                p.release();
                return result;
            }
            return std::unique_ptr<To, Deleter>(nullptr); // or throw std::bad_cast() if you prefer
        }
    
    
    auto pb = dynamic_unique_cast<B>(std::move(pa));
    
    0 讨论(0)
  • 2020-12-15 17:38

    This is dynamic_pointer_cast of boost. The idea is quite simple(but ignore the deleter).

    //dynamic_pointer_cast overload for std::unique_ptr
    template<class T, class U> std::unique_ptr<T> dynamic_pointer_cast( std::unique_ptr<U> && r ) BOOST_SP_NOEXCEPT
    {
        (void) dynamic_cast< T* >( static_cast< U* >( 0 ) );
    
        BOOST_STATIC_ASSERT_MSG( boost::has_virtual_destructor<T>::value, "The target of dynamic_pointer_cast must have a virtual destructor." );
    
        T * p = dynamic_cast<T*>( r.get() );
        if( p ) r.release();
        return std::unique_ptr<T>( p );
    }
    
    0 讨论(0)
提交回复
热议问题