2

我试图理解为什么bad_weak_ptr调用时会出现异常shared_from_this

#include <memory>
#include <iostream>

class parent : public std::enable_shared_from_this<parent>
{
public:
    void compare(std::shared_ptr<parent> const& p2)
    {
        std::cout << (this->shared_from_this() == p2->shared_from_this());
    }
};

class child1 : public parent
{};

class child2 : public parent
{};

class child3 : public child1, public child2
{};

void compare(parent& p1, parent& p2)
{
    std::cout << &p1 << " : " << &p2 << "\n";
    std::cout << (&p1 == &p2);
}

void compare(std::shared_ptr<parent> const& p1, std::shared_ptr<parent> const& p2)
{
    compare(*p1, *p2);
//  p1->compare(p2); // bad_weak_ptr
//  auto p = p1->shared_from_this(); // bad_weak_ptr
}

void compareusingchild(std::shared_ptr<child1> const& c1, std::shared_ptr<child2> const& c2)
{
    compare(c1, c2);
}

int main()
{
    std::shared_ptr<child3> c3 = std::make_shared<child3>();
    try
    {
        compareusingchild(c3, c3);
    }
    catch (std::exception& e)
    {
        std::cout << e.what();
    }
    return 0;
}

我发现通过使class parent继承虚拟化,这个问题似乎不会持续存在。为什么这不是编译时错误?当找不到正确的继承父级时,类似于“模棱两可的函数调用”?

仅包含父类的 API 无法提前知道继承层次结构,调用 compare 方法(在父类中)将导致运行时错误。是否可以在编译时检测到此类错误?

4

1 回答 1

1

好的,现在我知道是什么问题了。

钻石问题禁用shared_from_this()

在引擎盖下(对于 MSVC 2017),您可以找到如下内容:

template<class _Yty,
    class = void>
    struct _Can_enable_shared
        : false_type
    {   // detect unambiguous and accessible inheritance from enable_shared_from_this
    };

template<class _Yty>
    struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>
        : is_convertible<remove_cv_t<_Yty> *, typename _Yty::_Esft_type *>::type
    {   // is_convertible is necessary to verify unambiguous inheritance
    };

所以基本上在生成模板时,它会检查是否可以转换为child3 *to std::enable_shared_from_this<parent> *。如果可能,则设置内部弱指针,否则不做任何事情。

现在,由于存在歧义,因此不可能进行简单的转换,因此 std::is_convertible返回 false 并且shared_from_this未启用(设置为正确的值)。

这是一个证明:https ://godbolt.org/z/V2AzLk

        std::cout << "Conv child3: " << std::is_convertible<child3*, std::enable_shared_from_this<parent>*>::value << std::endl;
        std::cout << "Conv child2: " << std::is_convertible<child2*, std::enable_shared_from_this<parent>*>::value << std::endl;
        std::cout << "Conv child1: " << std::is_convertible<child1*, std::enable_shared_from_this<parent>*>::value << std::endl;

印刷:

Conv child3: 0
Conv child2: 1
Conv child1: 1

所以基本上歧义不会导致编译问题,它只是不启用此功能。

于 2019-08-29T10:33:36.063 回答