将从工厂方法创建的 unique_ptr 转换为 shared_ptr 并使用 shared_from_this 后遇到 std::bad_weak_ptr 异常

发布于 2025-01-13 19:56:01 字数 1442 浏览 3 评论 0原文

总之,我有一个从 std::enabled_shared_from_this 继承的类,并且有一个工厂方法返回它的 std::unique_ptr 。在另一个类中,我将前一个类对象的 std::unique_ptr 转换为 std::shared_ptr,然后调用 shared_from_this() ,然后抛出 std::bad_weak_ptr。代码如下所示:

#include <memory>
#include <iostream>

struct Executor;
struct Executor1 {
  Executor1(const std::shared_ptr<Executor>& executor,
            int x): parent(executor) {
    std::cout << x << std::endl;
  }
  std::shared_ptr<Executor> parent;
};

struct Backend {
  virtual ~Backend() {}
  virtual void run() = 0;
};

struct Executor: public Backend, public std::enable_shared_from_this<Executor> {
  const int data = 10;
  virtual void run() override {
    Executor1 x(shared_from_this(), data);
  }
};

// std::shared_ptr<Backend> createBackend() {
std::unique_ptr<Backend> createBackend() {
  return std::make_unique<Executor>();
}

class MainInstance {
private:
  std::shared_ptr<Backend> backend;
public:
  MainInstance(): backend(createBackend()) {
    backend->run();
  }
};

int main() {
  MainInstance m;
  return 0;
}

确实改变了 std::unique_ptr; createBackend()std::shared_ptr; createBackend()可以解决问题,但据我了解,一般来说,工厂模式应该更喜欢返回一个unique_ptr。考虑到软件工程的良好实践,是否有更好的解决方案?

In summary, I have a class inherited from std::enabled_shared_from_this, and there is a factory method return an std::unique_ptr of it. In another class, I convert the std::unique_ptr of the previous class object to std::shared_ptr, and then I call shared_from_this(), which then throws std::bad_weak_ptr. The code is shown below:

#include <memory>
#include <iostream>

struct Executor;
struct Executor1 {
  Executor1(const std::shared_ptr<Executor>& executor,
            int x): parent(executor) {
    std::cout << x << std::endl;
  }
  std::shared_ptr<Executor> parent;
};

struct Backend {
  virtual ~Backend() {}
  virtual void run() = 0;
};

struct Executor: public Backend, public std::enable_shared_from_this<Executor> {
  const int data = 10;
  virtual void run() override {
    Executor1 x(shared_from_this(), data);
  }
};

// std::shared_ptr<Backend> createBackend() {
std::unique_ptr<Backend> createBackend() {
  return std::make_unique<Executor>();
}

class MainInstance {
private:
  std::shared_ptr<Backend> backend;
public:
  MainInstance(): backend(createBackend()) {
    backend->run();
  }
};

int main() {
  MainInstance m;
  return 0;
}

Indeed changing std::unique_ptr<Backend> createBackend() to std::shared_ptr<Backend> createBackend() can solve the problem, but as I understand, in general, the factory pattern should prefer return a unique_ptr. Considering a good pratice of software engineering, is there a better solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

香橙ぽ 2025-01-20 19:56:01

[util.smartptr.shared.const]/1 在下面的构造函数定义中,为指针 p 启用 shared_from_this Y* 类型的 code>p 表示如果 Y 具有明确且可访问的基类,该基类是 enable_shared_from_this 的特化(23.11.2.5),然后[神奇的事情发生了,让 shared_from_this() 适用于 *p - IT]

模板 Shared_ptr(unique_ptr&r);

[util.smartptr.shared.const]/29 效果: ...相当于shared_ptr(r.release(), r.get_deleter())。 ..

模板 Shared_ptr(Y* p, D d);

[util.smartptr.shared.const]/10 效果: ... 使用 pshared_from_this >

您的示例执行 std::shared_ptr(uptr) 其中 uptrstd::unique_ptr,相当于 std::shared_ptr(p, d),其中 p 是类型后端*。此构造函数使用 p 启用 shared_from_this - 但这是无操作的,因为 Backend 没有明确且可访问的基类,该基类是enable_shared_from_this 的专门化

为了使 Executor::enable_from_this 工作,您需要传递给 shared_ptr构造函数 静态类型为 Executor*(或从中派生的某种类型)的指针。

[util.smartptr.shared.const]/1 In the constructor definitions below, enables shared_from_this with p, for a pointer p of type Y*, means that if Y has an unambiguous and accessible base class that is a specialization of enable_shared_from_this (23.11.2.5), then [magic happens that makes shared_from_this() work for *p - IT]

template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);

[util.smartptr.shared.const]/29 Effects: ... equivalent to shared_ptr(r.release(), r.get_deleter())...

template<class Y, class D> shared_ptr(Y* p, D d);

[util.smartptr.shared.const]/10 Effects: ... enable shared_from_this with p

Your example executes std::shared_ptr<Backend>(uptr) where uptr is std::unique_ptr<Backend>, which is equivalent to std::shared_ptr<Backend>(p, d) where p is of type Backend*. This constructor enables shared_from_this with p - but that's a no-op, as Backend doesn't have an unambiguous and accessible base class that is a specialization of enable_shared_from_this

In order for Executor::enable_from_this to work, you need to pass to a shared_ptr constructor a pointer whose static type is Executor* (or some type derived therefrom).

关于从前 2025-01-20 19:56:01

好吧,我找到了一个简单的解决方案,即使用 auto 作为工厂函数的返回类型,而不是 std::unique_ptrstd::shared_ptr,并将 std::make_unique 保留在工厂函数内。工厂函数createBackend应该是:

auto createBackend() {
  return std::make_unique<Executor>();
}

在这种情况下,可以自动确定返回类型,尽管我不知道它到底是如何工作的。此代码可以返回 unique_ptrshared_ptr,这应该比仅使用 shared_ptr 更好。我测试了 clang 和 gcc,它们都有效,但我仍然不确定类型推导和隐式转换是否能保证这一点。

更新
实际上,我发现 auto 将上面的返回类型推导为 std::unique_ptr 而不是 std::unique_ptr ,这可能是代码有效的原因。但使用 auto 有一个问题:如果您在 if-else 块中返回智能指针,其中返回类型根据某些参数而变化,则 auto 无法确定类型。例如:

std::unique_ptr<Backend> createBackend(int k = 0) {
  if (k == 0) {
    return std::make_unique<Executor>();
  }
  else {
    return std::make_unique<Intepreter>();
  }
}

这里,ExecutorIntepreter 都派生自 Backend。我认为正确的解决方案包括:

  1. 继承Backend而不是从std::enable_shared_from_this继承它的派生类;
  2. shared_from_this 之后使用 dynamic_pointer_castshared_ptr 转换为派生类。

完整代码列于:
https://gist.github.com/HanatoK/8d91a8ed71271e526d9becac0b20f758

Ok, I find a simple solution, that is, using auto as the return type of the factory function, instead of std::unique_ptr or std::shared_ptr, and keeping std::make_unique inside the factory function. The factory function createBackend should be:

auto createBackend() {
  return std::make_unique<Executor>();
}

In this case, the return type can be automatically determined, although I don't know how it works exactly. This code can return either unique_ptr or shared_ptr, which should be better than just using shared_ptr. I tested clang and gcc, and both of them worked, but I am still not sure if this is gauranteed by the type deduction and the implicit conversion.

Update:
Actually, I have found that auto deduces the return type above as std::unique_ptr<Executor> instead of std::unique_ptr<Backend>, which might be the reason why the code works. But using auto has an issue: if you return the smart pointer in an if-else block, where the return type varies depending on some parameters, then auto cannot determine the type. For example:

std::unique_ptr<Backend> createBackend(int k = 0) {
  if (k == 0) {
    return std::make_unique<Executor>();
  }
  else {
    return std::make_unique<Intepreter>();
  }
}

Here, both Executor and Intepreter derive from Backend. I think a correct solution includes:

  1. Inherit Backend instead of its derived classes from std::enable_shared_from_this;
  2. Use dynamic_pointer_cast<Derived class> to cast the shared_ptr to derived class after shared_from_this.

The full code is listed in:
https://gist.github.com/HanatoK/8d91a8ed71271e526d9becac0b20f758

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文