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?
CodePudding user response:
[util.smartptr.shared.const]/1 In the constructor definitions below, enables
shared_from_this
withp
, for a pointerp
of typeY*
, means that ifY
has an unambiguous and accessible base class that is a specialization ofenable_shared_from_this
(23.11.2.5), then [magic happens that makesshared_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
withp
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).
CodePudding user response:
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.