9

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?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Carbon
  • 3,828
  • 3
  • 24
  • 51

2 Answers2

8

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);
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • For completeness, `std::shared_ptr<...>`'s aliasing constructor also works. `std::shared_ptr c(b, static_cast(b.get()));` – evan Apr 28 '17 at 14:49
2

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302