10

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.

Artificial Mind
  • 945
  • 6
  • 19

2 Answers2

10

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.

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
  • Thanks for the explanation. While I still think that this is a subtle source of errors, I guess it cannot be detected by static assertions of any kind. – Artificial Mind Oct 13 '16 at 12:25
  • @ArtificialMind : they could have added something like this in the `std::enable_shared_from_this` implementation : `static_assert(std::is_convertible::value, "Needs public inheritance.");` (assuming the implementation mentioned above) – Sander De Dycker Oct 13 '16 at 12:41
  • In principle it should possible to do this with private inheritance with a crapload of non-portable `friend` declarations – Thomas Eding Aug 15 '23 at 03:11
2

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)

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455