4
votes

I want my builder method Class::create() to return the unique_ptr

Why?

1) This design is not enforcing the shared ownership (and atomic lock). unique_ptr is very efficient and has same size as void*

2) Still, it is easy to move to shared_ptr on the class user's side (but only when client would like to share the instance for multiple ownership)

So. I want to pay only for what I'm using

But. I need some class instance's methods to return "this" pointer. If I inherit class from enable_shared_from_this I just use return shared_from_this but I think it won't be related to the unique ownership

In other words, will the unique_ptr created somewhere in class (without using shared_from_this() of course) somehow be related to (potential) shared_ptr created from this unique_ptr? Or it will lead to double deletion problem?

2
TBH I am not sure this make much sense because this can easily be put into a unique_ptr and there is no problem to solve like there is with shared_ptr. - Galik
So let's see - I return the unique_ptr so some Owner class. Then it moves to the shared_ptr and gives the handle to other classes like OtherOwner so they keep strong shared reference. But how can I even find this shared group from the class instance itself? - barney
This doesn't make any semantic sense. If there is an object to invoke this member function on, then it's already owned by something. How can you obtain a unique owner for it in the general case? - StoryTeller - Unslander Monica
@StoryTeller It does. For example, I create Widgets inside a Controller class. And I want a Widget to be able to create hierarchy. So I build Widget with: shared_ptr widget1 = move(Widget::create()). Then auto child = widget1->createChild() where each child should receive parent's shared_ptr -> to be converted in weak_ptr inside the Child. This means that in createChild I have to access to shared_from_this() - barney
@StoryTeller-UnslanderMonica I know, a bit late, but: IMO, the actual question is: Is enable_shared_from this itself often used at the wrong places in terms of software design? The issue with shared_from_this is: It somehow forces the way a struct/class has to be used (intrusive) and this overlaps with the "inner" semantic meaning of classes. It's fast to tag classes with enable_shared_from_this, but it becomes messy as hell in doubt to re-design things as soon as you want to use the class within different context ranges. - Secundi

2 Answers

3
votes

Is there unique_from_this()?

No.

Or how return unique_ptr from class inherited from enable_shared_from_this

In general, nothing special has to be done to return a unique_ptr to a class inherited from enable_shared_from_this. But remember that shared_from_this must not be called on instances owned by unique_ptr. As such, this is unsafe.

You cannot create a unique_ptr to this in a safe way, whether enable_shared_from_this is inherited or not.

1) This design is not enforcing the shared ownership (and atomic lock). unique_ptr is very efficient and has same size as void*

And this lack of shared ownership, which makes unique_ptr fast, is also what makes unique_from_this impossible to implement.

2
votes

In other words, will the unique_ptr created somewhere in class (without using shared_from_this() of course) somehow be related to (potential) shared_ptr created from this unique_ptr? Or it will lead to double deletion problem?

shared_ptr constructors will detect your publicly accessible enable_shared_from_this base, so yes, if the unique_ptr properly releases its pointer you'll have no double deletion and the shared_from_this() member will work correctly (but you'll get UB if the client invokes it with no existing shared_ptr managing it):

auto unique = Class::create();
auto shared = std::shared_ptr<Class>{ std::move(unique) }; // ok, pass ownsership to shared
auto shared_again = shared->shared_from_this(); // ok

auto unique2 = Class::create();
auto badly_shared = unique2->shared_from_this(); // nope, UB
auto badly_shared_again = std::shared_ptr<Class>{ unique2.get() }; // nope, double deletion coming ...

Moreover, since C++17 shared_from_this() will throw bad_weak_ptr when invoked from an non-shared instance.