#include <memory>
#include <iostream>
struct A : public std::enable_shared_from_this<A>
{
~A()
{
auto this_ptr = shared_from_this(); // std::bad_weak_ptr exception here.
std::cout << "this: " << this_ptr;
}
};
int main()
{
auto a = std::make_shared<A>();
a.reset();
return 0;
}
I'm getting std::bad_weak_ptr
exception when calling shared_from_this()
. Is it by design? Yes, it might be dangerous as this pointer can't be used after the destructor returns, but I don't see a reason why it would be technically impossible to get the pointer here, since the shared pointer object obviously still exists and can be used. Is there any way to circumvent this, short of writing my own enable_shared_from_this
analog (which I would rather not do)?
There's a very good technical reason why it's not possible.
The
shared_ptr
might exist, but the reference count for theA
object has reached zero, that's why the destructor is being run. Once the reference count reaches zero it cannot be increased again (otherwise you could get ashared_ptr
that refers to an object that is either in the middle of running its destructor, or has already been destroyed).Calling
shared_from_this()
tries to increase the reference count and return ashared_ptr
that shares ownership with the current owner(s), but you can't increase the counter from zero to one, so it fails.In this very specific case (inside the object's destructor) you know the object hasn't been completely destroyed yet, but
enable_shared_from_this<A>
has no way to know who is calling theshared_from_this()
function, so can't know if it's happening in this very specific case or in some other piece of code outside the object's destructor (e.g. in another thread that will keep going after the destructor).If you could somehow make it work for this specific case and you got a
shared_ptr<A>
that referred to the object currently being destroyed, you could give thatshared_ptr
to something outside the destructor that stored it for later use. That would allow that other piece of code to access a danglingshared_ptr
, after the object has been destroyed. That would be a big hole in theshared_ptr
andweak_ptr
type system.[util.smartptr.enab]/7 describes the preconditions for
shared_from_this
:Since your object is being destroyed, it must be the case that there is no
shared_ptr
that owns it. Consequently, you cannot callshared_from_this
without violating that requirement resulting in undefined behavior.You can force it be allowed, but it's kinda "headshoot urself", I can't predict all consequences of that, but next code works as expected, allows to call shared_from_this() in dtors (you can replace calls to boost by malloc/free too):
shared_ptr::reset
's implementation is oftenshared_ptr().swap(*this)
.Which means the
shared_ptr
you are trying to copy is already in its destructor state which in turns decrement the shared count before calling your destructor. When you callenable_shared_from_this
it will try to promote theweak_ptr
stored within it by constructing ashared_ptr
from thatweak_ptr
which results in an exception when the count is 0.So to answer your question, there is no standard way of doing what you want if your standard library implementation doesn't behave in a way that authorise it (i don't know is it is mandated by the standard or not).
Now, here is a hack that works on my machine (clang/libc++):
But i'm not sure you can do anything useful with that since your owning
shared_ptr
that you copied is about to die and that copy doesn't share things with the new cleanshared_ptr
you get after thereset
call.