two shared_ptr from same enable_shared_from_this instance

Viewed 830

Given this class which is enable_shared_from_this:

class connection : public std::enable_shared_from_this<connection>
{
   //...
};

Suppose I create two instances of std::shared_ptr from the same connection* as follows:

std::shared_ptr<connection> rc(new connection);

std::shared_ptr<connection> fc(rc.get(), [](connection const * c) {
                                   std::cout << "fake delete" << std::endl;
                               });

So far its good, as the resource { connection* } is owned by a single shared_ptrrc to be precise, and fc just have a fake deleter.

After that, I do this:

auto sc = fc->shared_from_this();
//OR auto sc = rc->shared_from_this(); //does not make any difference!

Now which shared_ptrrc or fc — would sc share its reference-count with? In other words,

std::cout << rc->use_count() << std::endl;
std::cout << fc->use_count() << std::endl;

What should these print? I tested this code and found rc seems to have 2 references while fc just 1.

My question is, why is that? and what should be the correct behavior and its rationale?

I'm using C++11 and GCC 4.7.3.

2 Answers

The raw pointer overloads assume ownership of the pointed-to object. Therefore, constructing a shared_ptr using the raw pointer overload for an object that is already managed by a shared_ptr, such as by shared_ptr(ptr.get()) is likely to lead to undefined behavior, even if the object is of a type derived from std::enable_shared_from_this. -- http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr

In your case you get to shared pointers that have two different ownership information blocks but always increment the ref count of the first shared pointer instance of the class.

If you remove the 'fake deleter' you'l get a double free problem.

On C++11 (and more generally, before C++17)

the only thing we know is that:

[util.smartptr.enab] shared_from_this();:

Requires: enable_shared_from_this shall be an accessible base class of T. *this shall be a subobject of an object t of type T. There shall be at least one shared_ptr instance p that owns &t.

Returns: A shared_ptr object r that shares ownership with p.

Postconditions: r.get() == this.

that if read literally, implies that it's unspecified if fc->shared_from_this() returns a copy of rc or fc (although sane implementations will simply assign to the internal weak_ptr once, hence the behaviour should be the same as in the c++17 case, as you observed).


From C++17 on

the situation is clear: enable_shared_from_this is essentially a weak_ptr that gets assigned by the first shared_ptr constructor (or make_shared factory) that sees it's in an expired state.

Hence, rc sets the weak_ptr, fc doesn't, fc->shared_from_this() returns a copy of rc. fc.get() will return a zombie pointer as soon as all rc copies get destroyed. Your observed behaviour is the right one.

Note that all shared_ptr constructors taking a non null pointer (and at most a deleter and/or an allocator) will own the pointer and manage its lifetime, as if it were constructed anew, the only difference being that only the first one will assign to enable_shared_from_this's weak_ptr, if any.

Related