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
C/C++ Source Code Visualization
Move or Named Return Value Optimization (Nrvo)
A Good Example for Boost::Algorithm::Join
Debugging Template Instantiations
The Intersection of Two Sorted Arrays
How to Enable C++17 Support in VScode C++ Extension
Should We Generally Use Float Literals for Floats Instead of the Simpler Double Literals
Expansion with Variadic Templates
Setting the Cursor Position in a Win32 Console Application
How Does Overloading of Const and Non-Const Functions Work
Polymorphic_Allocator: When and Why Should I Use It
How to Change the Variable to Which a C++ Reference Refers
The Implementation of Std::Forward
Custom Manipulator for C++ iOStream
When Is a Function Try Block Useful