Search code examples
c++shared-ptr

std::shared_ptr which is null but not empty


Compare with: std::shared_ptr which is empty but not null

The std::shared_ptr<T> aliasing constructor lets us play interesting games. The above SO post discusses when the first argument is std::shared_ptr<void>{nullptr}. I'm interested in the reverse. Is this guaranteed to keep the pointed-to object alive even though the shared_ptr "is" nullptr (and would be completely unreachable if it weren't that we keep a reference to it)?:

std::shared_ptr<S> ps = std::make_shared<S>();
auto& s = *ps; // Keep a reference to the S.
auto p = std::shared_ptr<S>(ps, nullptr); // Aliasing c'tor with null pointer.
ps = nullptr;
assert(ps == nullptr);
assert(p == nullptr);
foo(s); //< Is the S still alive here?

https://godbolt.org/z/M19s54


Solution

  • Yes, the "null but not empty" shared_ptr will keep the object alive because it shares ownership with the shared_ptr it was constructed from. All shared_ptrs that share ownership with each other contribute to an atomic reference count stored in the control block, and only when this reference count hits zero is the owned object destroyed.

    For standardese, see [util.smartptr.shared.const]/14:

    template<class Y> shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;
    Constructs a shared_ptr instance that stores p and shares ownership with r

    There are no constraints specified on the value of p; it may therefore be any valid pointer value, including null or even a past-the-end pointer (though I'm not sure why you'd want to do this).

    Then see [util.smartptr.shared.dest]/(1.1):

    If *this is empty or shares ownership with another shared_ptr instance (use_count() > 1), there are no side effects.

    In other words, when ps is destroyed, it still shares ownership with p, so the object is not destroyed yet.

    The resulting object is not really unreachable in the usual sense, since it is still possible to destroy it. You just can't do anything else with it.