0%

[C++] 一定要 public 继承 std::enable_shared_from_this

TL;DR

std::enable_shared_from_this必须要public继承,否则调用shared_from_this()不会编译失败,但运行时会抛std::bad_weak_ptr的异常。

我看到项目中有个类是struct,成员都暴露在外面,感觉不太安全,就把它改成了class,保证了所有对其成员的访问都通过public方法。看起来是个无害的操作,结果 ci test 挂了一大片,报错是std::bad_weak_ptr

我一看这个类,还真是继承了std::enable_shared_from_this

1
2
3
class Task: std::enable_shared_from_this {
// ...
};

这个报错通常是因为一个类继承自std::enable_shared_from_this,但某个没有被包裹在std::shared_ptr中的实例调用了shared_from_this(),然后就炸了,因为此时std::enable_shared_from_this没办法返回一个安全的std::shared_ptr

但人肉确认过它的所有分配都是走std::shared_ptr,且为了确保这点,我还把Task的构造函数也藏起来了,对外只暴露一个static构造器:

1
2
3
4
5
6
7
8
9
class Task: std::enable_shared_from_this {
public:
template <typename... Args>
static std::shared_ptr<Task> newTask(Args... args) {
return std::shared_ptr(new Task(std::forward<Args>(args)...));
}
private:
Task(Arg0 arg0, Arg1 arg1);
};

再跑一遍,还是不行。

略过 debug 过程,最终我发现是新的Task类继承std::enable_shared_from_this的方式有问题:它在private继承。此时搞笑的事情发生了,我手头的 gcc7.5 的std::enable_shared_from_this的实现不会在这种情况下为Task实例构造必要的_M_weak_this成员,导致后续调用shared_from_this()时报错。

我觉得这里的设计不好,此时std::enable_shared_from_this已经失去作用了,但没有产生编译错误,而是延迟到运行时再报错,极大增加了 debug 的成本。按理说这里既然可以有不同的特化版本,就应该可以在编译期发现这类错误。

结论:

  1. struct默认继承级别是public,而class则是private
  2. 所有继承都应该显式写出继承级别。
  3. 继承std::enable_shared_from_this时请public继承。