How do I dynamic upcast and downcast with smart pointers?
Asked Answered
O

2

10

I have some code with a generic interface in which I need to cast up and down. I'm trying to convert to smart pointers now, but am running into some errors. The code below reproduces the problem. I'm using C++14, so I think this stuff is supposed to work automagically now?

#include <memory>
#include <iostream>
int main()
{
    std::shared_ptr<int> a(new int);
    *a = 5;
    std::shared_ptr<void> b = std::dynamic_pointer_cast<void>(a);
    std::shared_ptr<int> c = std::dynamic_pointer_cast<int>(b);
    std::cout << *c; //should be 5
    return 0;
}

However, when I try to compile this, I get:

Error   C2680   '_Elem1 *': invalid target type for dynamic_cast    
Error   C2681   'int *': invalid expression type for dynamic_cast   

Guessing I did something dumb in the syntax?

Oneeyed answered 28/4, 2017 at 14:7 Comment(4)
Dynamic cast applies to polymorphic types. Did you meant reinterpret cast?Odd
probably? let me tryOneeyed
oh right. Duh. void does not live in the type hierarchy of my stuff. (does void live in any type hierarchies?)Oneeyed
that's how I got this name --->Oneeyed
E
10

The rules are the same as for dumb pointers, except you have to use std::static_pointer_cast and std::dynamic_pointer_cast instead of static_cast and dynamic_cast. C++17 will introduce the counterpart for reinterpret_cast, but that is not needed for up- or down casts, nor for conversions from or to void.

How do I dynamic upcast ... with smart pointers?

Conversion up in type hierarchy is implicit, so there is no need for a cast:

struct A{
    virtual ~A(){}
};
struct B:A{};

auto b = std::make_shared<B>();
std::shared_ptr<A> a = b;

How do I dynamic ... downcast with smart pointers?

Using std::dynamic_pointer_cast if you're unsure whether the source points to the correct type. This is only possible if the inheritance is polymorphic (i.e. the base has at least one virtual function):

b = std::dynamic_pointer_cast<B>(a);

Or std::static_pointer_cast if you know that the type is correct:

b = std::static_pointer_cast<B>(a);

std::shared_ptr<void> b = std::dynamic_pointer_cast<void>(a);
std::shared_ptr<int> c = std::dynamic_pointer_cast<int>(b);

void and int do not have an inheritance relationship so they can not be up- or down casted.

However, all pointers are implicitly convertible to a void pointer, so an implicit conversion is possible:

 std::shared_ptr<void> b = a;

And, void pointers can be static casted to the original type, so you can use std::static_pointer_cast:

std::shared_ptr<int> c = std::static_pointer_cast<int>(b);
Enjambement answered 28/4, 2017 at 14:26 Comment(1)
For completeness, std::shared_ptr<...>'s aliasing constructor also works. std::shared_ptr<int> c(b, static_cast<int*>(b.get()));Lingulate
H
2

Since C++17, you have std::reinterpret_pointer_cast

Handmaiden answered 28/4, 2017 at 14:11 Comment(2)
Wouldn't static_pointer_cast be enough, given one type is void?Antibody
Indeed, with void type.Handmaiden

© 2022 - 2024 — McMap. All rights reserved.