75

Consider:

struct SomethingThatsABase
{
    virtual bool IsChildOne() const { return false; }
    virtual bool IsChildTwo() const { return false; }
};

struct ChildOne : public SomethingThatsABase
{
    virtual bool IsChildOne() const { return true; }
};

struct ChildTwo : public SomethingThatsABase
{
    virtual bool IsChildTwo() const { return true; }
};

void SomeClientExpectingAChildOne(std::shared_ptr<ChildOne> const& ptrOne)
{
    //Does stuff
}

void SomeClient(std::shared_ptr<SomethingThatsABase> const& ptr)
{
    if (ptr->IsChildOne())
    {
        SomeClientExpectingAChildOne(ptr); //Oops.
        //Hmm.. can't static_cast here, because we need a `shared_ptr` out of it.
    }
}

(Note that I can't simply do a std::shared_ptr<ChildOne>(static_cast<ChildOne*>(ptr.get())), because then the reference counts don't get shared between the two shared_ptrs)

Billy ONeal
  • 97,781
  • 45
  • 291
  • 525

3 Answers3

102

This ought to work:

if (ptr->IsChildOne())
{
    SomeClientExpectingAChildOne(std::static_pointer_cast<ChildOne>(ptr));
}
mwigdahl
  • 15,122
  • 7
  • 48
  • 62
  • 1
    Perfect! +1. (And probably the "green bent angle thing" soon too) – Billy ONeal Jul 22 '11 at 20:26
  • 3
    +1 - I wasn't aware of `std::static_pointer_cast`! How does this work, does the returned `shared_ptr` act like a proxy so that the two shared pointers maintain the same reference count or so? – Frerich Raabe Jul 22 '11 at 20:29
  • 4
    @Frerich: A `shared_ptr` has two pointers -- one to the shared object, and one to the block with the strong and weak reference counts. Two `shared_ptr`s of different type can share the strong and weak reference count blocks without difficulty. My guess is that it's implemented with `static_pointer_cast` being a `friend` of `shared_ptr`, but of course your implementation may vary. – Billy ONeal Jul 22 '11 at 20:38
  • 2
    I've always used `shared_dynamic_cast` in Boost. Apparently `dynamic_pointer_cast` is more generic, however, because it works with various different types of pointers (`shared_ptr`, raw pointers, `intrusive_ptr`, and potentially anything else in the future). – Tamas Demjen Jul 22 '11 at 23:11
  • @FrerichRaabe, @billy-oneal, probably it uses the `aliasing constructor`, (ref, ptr) create a shared_pointer copy of `ref` but pointing to `ptr`. – Zhen Oct 05 '16 at 07:48
39

The shared_ptr equivalent of static_cast is static_pointer_cast, and the shared_ptr equivalent of dynamic_cast is dynamic_pointer_cast.

Billy ONeal
  • 97,781
  • 45
  • 291
  • 525
Joel
  • 570
  • 3
  • 5
  • 1
    I was unaware of both of these, so I went looking for some [documentation](http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/shared_ptr.htm#static_pointer_cast). Cool stuff! – Greg Howell Jul 22 '11 at 20:30
25

Starting from C++11, §20.10.2.2.9 ([util.smartptr.shared.cast]) of the C++ standard specifies the equivalents of static_cast, const_cast and dynamic_cast for std::shared_ptr to be as follows:

std::static_pointer_cast:

template <class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;

static_pointer_cast requires static_cast<T *>(r.get()) to be well formed. If r is empty, an empty shared_ptr<T> is returned, otherwise returns a pointer w sharing ownership with r where w.get() == static_cast<T *>(r.get()) and w.use_count() == r.use_count().

std::const_pointer_cast:

template <class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;

const_pointer_cast has similar requirements and semantics to static_pointer_cast, except that const_cast is used instead of static_cast.

std::dynamic_pointer_cast:

template <class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;

dynamic_pointer_cast is a bit different as it requires dynamic_cast<T *>(r.get()) to be well formed and have well defined semantics. If dynamic_cast<T *>(r.get()) is a non-zero value, returns a pointer w sharing ownership with r where w.get() == dynamic_cast<T *>(r.get()) and w.use_count() == r.use_count(), otherwise an empty shared_ptr<T> is returned.

std::reinterpret_pointer_cast:

For C++17, N3920 (adopted into Library Fundamentals TS in February 2014) also proposed a std::reinterpret_pointer_cast similar to the above, which would only require reinterpret_cast<T *>((U *) 0) to be well formed and returns shared_ptr<T>(r, reinterpret_cast<typename shared_ptr<T>::element_type *>(r.get())). Note N3920 also changed the wording for the other shared_ptr casts and extended shared_ptr to support arrays.

jotik
  • 14,982
  • 9
  • 48
  • 106