Downcasting Shared_Ptr<Base> to Shared_Ptr<Derived>

How can I cast a base smart pointer into the derived one in C++?

In C++11, there is the dynamic_pointer_cast

Which you can use:

void func(std::shared_ptr<Base> b){
b->VirtualBaseFunction();
if(auto d = dynamic_pointer_cast<Derived>(b)){
d->DerivedSpecificFunction():
....more code
}
}

Passing shared_ptrDerived as shared_ptrBase

Although Base and Derived are covariant and raw pointers to them will act accordingly, shared_ptr<Base> and shared_ptr<Derived> are not covariant. The dynamic_pointer_cast is the correct and simplest way to handle this problem.

(Edit: static_pointer_cast would be more appropriate because you're casting from derived to base, which is safe and doesn't require runtime checks. See comments below.)

However, if your foo() function doesn't wish to take part in extending the lifetime (or, rather, take part in the shared ownership of the object), then its best to accept a const Base& and dereference the shared_ptr when passing it to foo().

void foo(const Base& base);
[...]
shared_ptr<Derived> spDerived = getDerived();
foo(*spDerived);

As an aside, because shared_ptr types cannot be covariant, the rules of implicit conversions across covariant return types does not apply when returning types of shared_ptr<T>.

How is it possible that std::shared_ptrDerived casts to std::shared_ptrBase with no compiler errors?

std::shared_ptr has a constructor (from cppreference):

template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept; (9)

This overload ...

Constructs a shared_ptr which shares ownership of the object managed
by r. If r manages no object, this manages no object too. The
template overload doesn't participate in overload resolution if Y
is
not implicitly convertible to (until C++17)compatible with (since
C++17) T*.

Hence, in some sense the tricky part is not to convert the shared pointer, but to prevent it when the pointer types are not implicitly convertible. You can use SFINAE to achieve that.

Here is a toy example that has conversions from Bar<T1> to Bar<T2> (but not the other way around) enabled only when T1 inherits from T2:

#include <type_traits>

template <typename T1>
struct Bar {
Bar() {}

template <typename T2, typename std::enable_if_t<std::is_base_of_v<T1,T2>,int> = 0>
Bar(Bar<T2>){}

};

struct Foo {};
struct Derived : Foo {};

int main(){
Bar<Derived> d;
Bar<Foo> b;
//d = b; // eror
b = d; // OK
}

Live Demo

You probably want it more general, like shared pointer, to allow such conversion whenever a T2* can be converted to a T1*, not only when they inherit from each other (see std::is_convertible, I have to admit, I don't really understand the change that came with C++17, so I can only guess: maybe its std::is_layout_compatible in that case). So to mimic a pre-C++17 smart pointer you could use:

    template <typename T2, typename std::enable_if_t<std::is_convertible_v<T2*,T1*>,int> = 0>
Bar(Bar<T2>){}

which enables the conversion for all T2 where T2* can be converted to a T1*.

Downcasting shared pointer to derived class with additional functionality - is this safe?

Does the Base has a virtual destructor? If yes then it is safe to use downcasting. In your incorrect sample pDerived should be NULL in result, so you need to check the result of dynamic_pointer_cast every time.

How to cast a shared_ptrA to shared_ptrB where B is derived from A?

You can use std::dynamic_pointer_cast.

You use it like this:

std::shared_ptr<Base> basePtr;
std::shared_ptr<Derived> derivedPtr = std::dynamic_pointer_cast<Derived>(basePtr);

How to downcast shared_ptr without std::static_pointer_cast in C++?

std::shared_ptr<firstCaseType>(static_cast<firstCaseType*>(command.context.get()))

This extracts a non-owning raw pointer from context's ownership network, and passes it to a new std::shared_ptr as if it were owning. The solution is to use std::shared_ptr's aliasing constructor (overload #8 here):

std::shared_ptr<firstCaseType>(command.context, static_cast<firstCaseType*>(command.context.get()))
// ^^^^^^^^^^^^^^^



Related Topics



Leave a reply



Submit