c++c++11destructorshared-ptrweak-ptr

std::enable_shared_from_this: is it allowed to call shared_from_this() in destructor?


#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)?


Solution

  • 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.

    There's a very good technical reason why it's not possible.

    The shared_ptr might exist, but the reference count for the A 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 a shared_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 a shared_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 the shared_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 that shared_ptr to something outside the destructor that stored it for later use. That would allow that other piece of code to access a dangling shared_ptr, after the object has been destroyed. That would be a big hole in the shared_ptr and weak_ptr type system.