Prismatic
Prismatic

Reputation: 3348

Implicit casting of a derived pointer to a reference of its corresponding base

I have a function that looks like:

// this function might modify base_ptr
void SomeFunction(shared_ptr<Base> &base_ptr)
{ if(some_condition) { base_ptr = some_other_ptr; } }

I'd like to call the function with a shared_ptr:

shared_ptr<Derived> d = ...;
SomeFunction(d);

This doesn't work though. It doesn't work if I'm using normal pointers either (ie. implicit casting to Base* & from Derived*. One workaround is to create a Base pointer from the Derived one, and then pass that to the function.

shared_ptr<Base> base = d;
SomeFunction(b);

But this isn't very pretty from a coding standpoint. It also adds confusion and the potential for a subtle bug:

shared_ptr<Derived> d = derived;
shared_ptr<Base> b = derived;
SomeFunction(b);
// b and d might now be pointing to different things -- not good!

Is there better way to do this?

Upvotes: 2

Views: 77

Answers (2)

Marco A.
Marco A.

Reputation: 43662

You could template the function for the smart pointer's type

#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;

class Base {
    public:

    virtual void hello() {
        cout << "hello base" << endl;
    }
};

class Derived : public Base {
    public:

    void hello() {
        cout << "hello derived" << endl;
    }
};

class otherClass {
    public:
};

template<typename T>
void SomeFunction(shared_ptr<T> &base_ptr)
{ 
    static_assert(is_base_of<Base, T>::value == true, "Wrong non-derived type");

    base_ptr->hello();

    // Rebase it
    base_ptr.reset(new Derived);

    base_ptr->hello();
}

int main() {

    shared_ptr<Base> obj(new Base());

    SomeFunction(obj);
    // hello base
    // hello derived

    shared_ptr<Derived> obj2(new Derived());
    // hello derived
    // hello derived

    SomeFunction(obj2);

    shared_ptr<otherClass> obj3(new otherClass());
    SomeFunction(obj3); // ASSERT

    return 0;
}

http://ideone.com/ATqhEZ

If you intend to update all the smart pointers when you reset one, you'll have to do some book-keeping by yourself since they're not designed to have a "signal-like" mechanism to notify other smart pointers pointing to the same object.


Edited my answer to provide compile-time safety if you intend to use it with Base and relative subclasses.

Warning: the above solution uses C++11, the same could be accomplished in a similar way in pre-C++11 code

Upvotes: 1

Vaughn Cato
Vaughn Cato

Reputation: 64308

What you are trying to do is inherently dangerous, and C++ is making it hard on purpose. Consider if C++ allowed you to call SomeFunction the way you wanted. Then you could do this:

struct Base {
};

struct Derived1 : Base {
  void f1();
};

struct Derived2 : Base {
  void f2();
};

void SomeFunction(shared_ptr<Base> &p)
{
  p = make_shared<Derived2>(); // nothing wrong with converting 
                               // a Derived2 pointer to a Base pointer.
}

int main()
{
  shared_ptr<Derived1> d = make_shared<Derived1>();
  SomeFunction(d); // An error, but what if it wasn't?
  d->f1(); // Trying to call method f1 of a Derived2!
}

The compiler would not be able to know that d changed from a Derived1 pointer to a Derived2 pointer, so it wouldn't be able to give you a compile error when you tried to call method f1 of a Derived2.

Upvotes: 2

Related Questions