Bad_weak_ptr由调用具有多重继承的shared_from_this引起的

Bad_weak_ptr caused by call to shared_from_this with multiple inheritence

本文关键字:shared from 多重继承 this ptr weak 调用 Bad      更新时间:2023-10-16

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

#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 无法提前知道继承层次结构,并且调用比较方法(在父类中(将导致运行时错误。是否可以使此类错误在编译时可检测?

好的,现在我明白问题出在哪里了。

钻石问题禁用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 *转换为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

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