std::enable_shared_from_this: 是否允许在析构函数中调用 shared_from_this()?

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;
}

我在调用 shared_from_this() 时收到 std::bad_weak_ptr 异常。是设计使然吗?是的,这可能很危险,因为这个指针不能在析构函数 returns 之后使用,但我看不出为什么在技术上不可能在这里获取指针的原因,因为共享指针对象显然仍然存在并且可以使用。除了编写我自己的 enable_shared_from_this 模拟(我宁愿不这样做)之外,有什么办法可以避免这种情况?

shared_ptr::reset的实现往往是shared_ptr().swap(*this)

这意味着您尝试复制的 shared_ptr 已经处于其析构函数状态,这会在调用您的析构函数之前递减共享计数。当您调用 enable_shared_from_this 时,它将尝试通过从 weak_ptr 构造一个 shared_ptr 来提升存储在其中的 weak_ptr ,当计数为 0 时会导致异常。

所以为了回答你的问题,如果你的标准库实现没有以授权它的方式运行(我不知道它是由标准强制执行还是不是)。

现在,这是一个可以在我的机器上运行的技巧 (clang/libc++):

#include <memory>
#include <iostream>

class   hack_tag
{
};

namespace std
{

  template<>
  class shared_ptr<hack_tag>
  {
  public:
    template<typename T>
    weak_ptr<T>        extract_weak(const enable_shared_from_this<T>& shared)
    {
      return shared.__weak_this_;
    }
  };

};

using weak_ptr_extractor = std::shared_ptr<hack_tag>;

class   test : public std::enable_shared_from_this<test>
{
public:
  test()
  {
    std::cout << "ctor" << std::endl;
  }

  ~test()
  {
    std::cout << "dtor" << std::endl;
    weak_ptr_extractor  hacker;
    auto weak = hacker.extract_weak(*this);
    std::cout << weak.use_count() << std::endl;
    auto shared = weak.lock();
  }
};


int     main(void)
{
  std::shared_ptr<test>  ptr = std::make_shared<test>();

  ptr.reset();
}

但我不确定您是否可以用它做任何有用的事情,因为您拥有的 shared_ptr 您复制的内容即将失效,并且该副本不会与新的干净 shared_ptr 共享内容你在 reset 电话后得到。

[util.smartptr.enab]/7 描述了 shared_from_this 的前提条件:

Requires: enable_shared_from_this<T> shall be an accessible base class of T. *this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t. [emph. added]

既然你的对象正在被销毁,那么肯定没有 shared_ptr 拥有它。因此,您不能在不违反导致未定义行为的要求的情况下调用 shared_from_this

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.

这是不可能的一个很好的技术原因。

shared_ptr 可能存在,但 A 对象的引用计数已达到零,这就是析构函数 运行 的原因。一旦引用计数达到零,它就不能再次增加(否则你可能会得到一个 shared_ptr ,它引用一个对象,该对象要么在 运行 析构函数的中间,要么已经被销毁了)。

调用 shared_from_this() 尝试增加引用计数和 return 与当前所有者共享所有权的 shared_ptr,但您不能将计数器从零增加到一个,所以它失败了。

这种非常特殊的情况下(在对象的析构函数中)你知道对象还没有被完全销毁,但是enable_shared_from_this<A>无法知道是谁正在调用 shared_from_this() 函数,所以不知道它是发生在 这个非常特殊的情况 中还是在对象析构函数之外的其他代码段中(例如在另一个线程中将在析构函数之后继续执行)。

如果你能以某种方式使它适用于这种特定情况并且你得到一个 shared_ptr<A> 引用当前被销毁的对象,你可以将 shared_ptr 给存储在析构函数之外的东西以备后用。这将允许另一段代码在对象被销毁后访问悬空 shared_ptr。这将是 shared_ptrweak_ptr 类型系统中的一个大漏洞。

你可以强制允许它,但它有点 "headshoot urself",我无法预测所有后果,但下一个代码按预期工作,允许在 dtors 中调用 shared_from_this() (您也可以用 malloc/free 替换对 boost 的调用):

template<class GenT, typename... Args>
struct AllocSharedObj
{
    static std::shared_ptr<GenT> alloc(Args&&... args)
    {

        using pool_t = boost::singleton_pool<GenT, sizeof(GenT)>;
        void *mem = pool_t::malloc();
        //log_create_delete(true);
        auto r = std::shared_ptr<GenT>(new (mem) GenT(std::forward<Args>(args)...), [](GenT * p)
        {
            if (p)
            {
                //log_create_delete(false);

                //dirty hack, allowing to call SHARED_FROM_THIS inside that functions >:
                auto cheat = std::shared_ptr<GenT>(p, [](auto) {});
                p->~GenT();
                cheat = nullptr;

                pool_t::free(p);
            }
        });
        //here can be post-constructor init which needs shared_from_this like
        r->init();
        return r;
    }
};