If you make a class T
that derives from std::enable_
, then the creation of a std::shared_ptr
to that class will activate the shared_
method to return a shared_ptr
that shares ownership with the originally-created std::shared_ptr
.
The catch is that the shared_ptr
constructor and enable_
are in cahoots, and the shared_ptr
must be able to access the enable_
in order to finish the job. This requires that you publicly derive from std::enable_
:
class MyClass : public std::enable_shared_from_this<MyClass>
{
...
};
If you forget the public
keyword, then the base class defaults to private
, and the secret signal between shared_ptr
and enable_
does not get through.
Here’s how enable_
and shared_ptr
work together. Note that I’ve ignored edge cases; the idea here is to give the basic idea so you can diagnose enable_
issues yourself.
template<typename T> struct enable_shared_from_this { shared_ptr<T> shared_from_this() { return shared_ptr<T>(weak_this); } weak_ptr<T> weak_this; }; template<typename T> struct shared_ptr { shared_ptr(T* p) : ptr(p) { if (T derives from enable_shared_from_this) { ptr->weak_ptr = *this; } } T* ptr; /* other stuff */ };
When a shared_ptr
is created, it snoops at the managed object to see if it derives from enable_
. If so, then it sets the weak_ptr
to hold a weak pointer to the shared object. When you later ask for a shared_
, it promotes this weak pointer to a shared pointer and returns it.
Okay, so we already see some consequences and pitfalls:
First of all, if you fail to derive publicly from enable_
, the feature simply fails silently. There is no diagnostic that says, “Hey, like, you’re deriving from enable_
, but you did it privately, so it’s not going to work.”¹
Second, notice that the weak pointer is set only when the object is placed inside a shared_ptr
, which happens after the shared object has been constructed. This means that you cannot use shared_
in your constructor.
Third, if the object is not wrapped inside a shared_
at all, then shared_
will always fail. For example, if somebody constructs the object on the stack, or via new
or make_unique
, it will not be controlled by a shared_
.
There are so many ways enable_
can go wrong. Next time, we’ll see what we can do to guard against them.
¹ Maybe it’s possible to add a diagnostic to shared_
. I wonder if the shared type is required to be complete by that point.
0 comments