Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shared_from_this and private inheritance

The cpp reference has this example of how to use std::enable_shared_from_this (slightly adjusted)

class Good : std::enable_shared_from_this<Good>
{
    public: std::shared_ptr<Good> getptr() { return shared_from_this(); }
};

...

auto good = std::make_shared<Good>();
good->getptr();

However, this does NOT work in Visual Studio 2015 (Enterprise, Version 14.0.25123.00 Update 2), i.e. a std::bad_weak_ptr exception is thrown.

Looking at other examples (including different ones from cpp reference or Microsoft) I noticed that they use public inheritance instead of private one. And using public inheritance actually solves my problem (no std::bad_weak_ptr anymore but a valid shared_ptr instead).

Cpp reference does not mention that I have to publicly inherit from std::enable_shared_from_this, so where is the error? Is Visual Studio's behavior wrong (I guess there is a visibility problem when using private inheritance) or did cpp reference fail to mention this limitation?

PS: make_shared<good>() or shared_ptr<Good>(new Good) doesn't make a difference.

PSS: Both versions compile just fine, the private one just doesn't work, making this a quite nasty kind of bug.

EDIT: Changed struct to class. Cpp reference actually uses public inheritance in its examples. Still, no word that it has to be public. It is actually listed there, I just have to learn to read carefully. Thanks @Angew.

like image 241
Artificial Mind Avatar asked Oct 13 '16 11:10

Artificial Mind


2 Answers

A typical implementation of std::enable_shared_from_this, requires the std::shared_ptr constructor (called by std::make_shared in your case) to be able to detect the presence of a std::enable_shared_from_this base, so it can set the std::weak_ptr member of that base.

With private inheritance, that's not possible, so you get the runtime exception you got when calling shared_from_this (because the std::weak_ptr was never set, since the std::shared_ptr constructor couldn't detect the std::enable_shared_from_this base).

The C++ standard mentions such an implementation :

[ Note: A possible implementation is shown below:

template<class T> class enable_shared_from_this {
private:
  weak_ptr<T> __weak_this;
protected:
  constexpr enable_shared_from_this() : __weak_this() { }
  enable_shared_from_this(enable_shared_from_this const &) { }
  enable_shared_from_this& operator=(enable_shared_from_this const &) { return *this; }
  ~enable_shared_from_this() { }
public:
  shared_ptr<T> shared_from_this() { return shared_ptr<T>(__weak_this); }
  shared_ptr<T const> shared_from_this() const { return shared_ptr<T const>(__weak_this); }
};

The shared_ptr constructors that create unique pointers can detect the presence of an enable_shared_from_this base and assign the newly created shared_ptr to its __weak_this member. — end note ]

The cppreference page you linked to also mentions this in the notes.

like image 130
Sander De Dycker Avatar answered Oct 17 '22 04:10

Sander De Dycker


The code in your question doesn't use private inheritance at all: struct defaults to public access control, both for members and for base classes.

Furthermore, cppreference does not omit anything. The text of the page clearly states:

Publicly inheriting from std::enable_shared_from_this<T> ...

(Emphasis mine)

like image 29
Angew is no longer proud of SO Avatar answered Oct 17 '22 04:10

Angew is no longer proud of SO