Bad_weak_ptr 由使用多重继承调用 shared_from_this 引起
Bad_weak_ptr caused by call to shared_from_this with multiple inheritence
我想了解为什么在调用 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
继承虚拟化,这个问题似乎不会持续存在。为什么这不是编译时错误?当找不到正确的继承 parent?
时,类似于 'ambiguous function call'
仅包含 parent class 的 API 无法提前知道继承层次结构和调用比较方法(在 parent 中)将导致 run-time 错误。是否有可能使编译时检测到此类错误?
好的,现在我知道是什么问题了。
钻石问题禁用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
returns 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
所以基本上歧义不会导致编译问题它只是不启用此功能。
我想了解为什么在调用 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
继承虚拟化,这个问题似乎不会持续存在。为什么这不是编译时错误?当找不到正确的继承 parent?
仅包含 parent class 的 API 无法提前知道继承层次结构和调用比较方法(在 parent 中)将导致 run-time 错误。是否有可能使编译时检测到此类错误?
好的,现在我知道是什么问题了。
钻石问题禁用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
returns 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
所以基本上歧义不会导致编译问题它只是不启用此功能。