c++shared-ptrmultiple-inheritanceweak-ptrenable-shared-from-this

How can I use std::enable_shared_from_this in both super and subclass?


I have two classes A and B, where B is a subclass of A. I need both classes to use std::enable_shared_from_this.

I tried this:

#include <memory>
#include <iostream>
#include <vector>


class A : public std::enable_shared_from_this<A> {
  public:
    void insertme(std::vector<std::shared_ptr<A>>& v) {
        std::cout << "A::insertme\n";
        v.push_back(shared_from_this());
        std::cout << "OK\n";
    }
};

class B : public A, public std::enable_shared_from_this<B> {
  public:
    void insertme(std::vector<std::shared_ptr<B>>& v) {
        std::cout << "B::insertme\n";
        v.push_back(std::enable_shared_from_this<B>::shared_from_this());
        std::cout << "OK\n";
    }
};

int main()
{
    std::vector<std::shared_ptr<A>> va;
    std::vector<std::shared_ptr<B>> vb;

    std::shared_ptr<A> pa = std::make_shared<A>();
    std::shared_ptr<B> pb = std::make_shared<B>();

    pa->insertme(va);
    pb->insertme(vb);
}

(In order to avoid that shared_from_this() be ambiguous, I had to fully qualify it in B::insertme.)

When I run the above program, I get this output:

A::insertme
OK
B::insertme
terminate called after throwing an instance of 'std::bad_weak_ptr'
  what():  bad_weak_ptr
Aborted (core dumped)

So A::insertme works, but B::insertme does not.

I'm using GCC 9.1.0 under Linux.

What am I doing wrong?


Solution

  • You only need to (and only can) inherit from shared_from_this in the base class:

    class A : public std::enable_shared_from_this<A> {
      public:
        void insertme(std::vector<std::shared_ptr<A>>& v) {
            std::cout << "A::insertme\n";
            v.push_back(shared_from_this());
            std::cout << "OK\n";
        }
    };
    
    class B : public A {
      public:
        void insertme(std::vector<std::shared_ptr<B>>& v) {
            std::cout << "B::insertme\n";
            v.push_back(std::static_pointer_cast<B>(shared_from_this()));
            std::cout << "OK\n";
        }
    };
    

    This means you need the explicit static_pointer_cast to get a shared_ptr<B>, but you could wrap that into an override in B if you want:

    std::shared_ptr<B> shared_from_this() { return std::static_pointer_cast<B>(A::shared_from_this()); }