Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this trick, to make calling shared_from_this() in the constructor 'just work', dangerous?

Question for the C++ experts.

We all know that calling shared_from_this() in the class constructor will result in a bad_weak_ptr exception, because no shared_ptr to the instance has been created yet.

As a work-around for that, I came up with this trick:

class MyClass : public std::enable_shared_from_this<MyClass>
{
public:
    MyClass() {}

    MyClass( const MyClass& parent )
    {
        // Create a temporary shared pointer with a null-deleter
        // to prevent the instance from being destroyed when it
        // goes out of scope:
        auto ptr = std::shared_ptr<MyClass>( this, [](MyClass*){} );

        // We can now call shared_from_this() in the constructor:
        parent->addChild( shared_from_this() );
    }

    virtual ~MyClass() {}
};

Someone argued that this is not safe, as the object has not yet been fully formed. Is he right about that?

I'm not using 'this' to access member variables or functions. Furthermore, all member variables have already been initialized, provided I used initializer lists. I don't see how this trick could be unsafe.

Edit: it turns out this trick indeed creates unwanted side-effects. The shared_from_this() will point to the temporary shared_ptr and if you're not careful, the parent-child relationship in my sample code will break. The implementation of enable_shared_from_this() simply does not allow it. Thanks, Sehe, for pointing me in the right direction.

like image 439
Paul Houx Avatar asked Oct 11 '15 20:10

Paul Houx


1 Answers

That's not dangerous.

The documented restriction is: cppreference

Before calling shared_from_this, there should be at least one std::shared_ptr p that owns *this

Nowhere does it say that it can't be used from inside the constructor /for this reason/.

It's just a-typical. That's because under normal circumstances, a make_shared or shared_pointer<T>(new T) cannot complete before the T constructor has exited.

Caveat: the object isn't fully formed so you cannot legally invoke any virtual methods (at the penalty of Undefined Behaviour).


Guideline Since it's possible to use this class wrong (e.g. using shared_ptr<T>(new T) which creates a second shared_ptr with the same underlying pointer value... oops) you should prefer a design that prevents this.

Using a friend factory function that returns the shared_ptr<T> could be one approach.

--> See also The Pit Of Success

like image 177
sehe Avatar answered Sep 20 '22 19:09

sehe