7

I have a base class which derives from boost::enable_shared_from_this, and then another class which derives from both the base class and boost::enable_shared_from_this:

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

using namespace boost;

class A : public enable_shared_from_this<A> { };

class B : public A , public enable_shared_from_this<B> {
public:
    using enable_shared_from_this<B>::shared_from_this;
};

int main() {
shared_ptr<B> b = shared_ptr<B>(new B());
shared_ptr<B> b_ = b->shared_from_this();

return 0;
}

This compiles but at runtime it gives

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_weak_ptr> >'
  what():  tr1::bad_weak_ptr
Aborted

What is causing this, and is there some way around it?

EDIT:

What if I need something like this:

class A : public enable_shared_from_this<A> { };
class B : public enable_shared_from_this<B> { };    

class C : public A, public B, public enable_shared_from_this<C> {
public:
    using enable_shared_from_this<C>::shared_from_this;
};

such that A and B both need shared_from_this on their own (and one can't inherit it from the other), and C needs A, B, and shared_from_this?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Ken
  • 619
  • 1
  • 7
  • 11
  • I know that b_ would be the same as b and in this case I could just use b. But this is just a demonstration; other cases in which I actually need to use shared_from_this will cause the same error. – Ken Oct 08 '12 at 23:28
  • My solution in that case would involve virtual inheritance. I suspect boost may already have a class for this, perhaps a non-templated version of `enable_shared_from_this`. But if it didn't, I'd create my own and virtually inherit from it anywhere I needed the functionality. You'd have to use `dynamic_pointer_cast` instead of `static_pointer_cast` of course. But it'd work. – Omnifarious Oct 09 '12 at 01:04
  • Yeah - I can't see an obviously clean way to make this work. It's close to diamond inheritance, and probably suggests that a rethink of the design is in order. – Fraser Oct 09 '12 at 01:16

2 Answers2

11

You shouldn't inherit from enable_shared_from_this more than once in a given inheritance chain.

In this case, you can leave the base class A inheriting from enable_shared_from_this, and have the derived class B return a shared_ptr<A> and then static_pointer_cast it to shared_ptr<B>.

Or as Omnifarious pointed out, you could have a function in B which does this for you. Although, rather than overloading shared_from_this() I would favour explicitly-named functions to minimise surprises for clients of the class:

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

using boost::shared_ptr;

class A : public boost::enable_shared_from_this<A> { };

class B : public A {
public:
    using enable_shared_from_this<A>::shared_from_this;
    shared_ptr<B> shared_B_from_this() {
        return boost::static_pointer_cast<B>(shared_from_this());
    }
    shared_ptr<B const> shared_B_from_this() const {
        return boost::static_pointer_cast<B const>(shared_from_this());
    }
};

int main() {
    shared_ptr<B> b = shared_ptr<B>(new B);
    shared_ptr<B> b1 = boost::static_pointer_cast<B>(b->shared_from_this());
    shared_ptr<B> b2 = b->shared_B_from_this();
    return 0;
}
Fraser
  • 74,704
  • 20
  • 238
  • 215
5

Here's how I would solve your problem:

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

using namespace boost;

class virt_enable_shared_from_this :
   public enable_shared_from_this<virt_enable_shared_from_this>
{
 public:
   virtual ~virt_enable_shared_from_this() {}
};

template <class T>
class my_enable_shared_from_this : virtual public virt_enable_shared_from_this
{
 public:
   shared_ptr<T> shared_from_this() {
      return dynamic_pointer_cast<T>(virt_enable_shared_from_this::shared_from_this());
   }
};

class A : public my_enable_shared_from_this<A> { };

class B : public my_enable_shared_from_this<B> { };

class C : public A, public B, public my_enable_shared_from_this<C> {
 public:
   using my_enable_shared_from_this<C>::shared_from_this;
};

int main() {
   shared_ptr<C> c = shared_ptr<C>(new C());
   shared_ptr<C> c_ = c->shared_from_this();

   return 0;
}

This is painful and at least a bit ugly. But it works reasonably well, after a fashion. I think Fraser's idea of re-thinking your design is likely the better option.

Community
  • 1
  • 1
Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • I don't understand the purpose of `virt_enable_shared_from_this` without it inheriting `virtual`ly from `enable_shared_from_this`. – Matthieu M. Oct 09 '12 at 07:34
  • @MatthieuM.: `enable_shared_from_this` is a template. So everybody virtually inheriting from a template isn't going to help the situation because each of those template instances is different. `virt_enable_shared_from_this` is to give a common base class for everybody to virtually inherit from. – Omnifarious Oct 09 '12 at 15:39
  • I think you could replace `dynamic_pointer_cast` with `static_pointer_cast` it is safe here...and faster – kassak Mar 21 '13 at 14:17
  • @kassak: Maybe. I don't want to risk it. virtual base classes can float around inside the class layout in interesting ways. – Omnifarious Mar 21 '13 at 16:30