25

I just cannot understand how std::enable_shared_from_this::shared_from_this returns a shared pinter that shared ownership with existing pointer. In other words you do this:

std::shared_ptr<Foo> getFoo() { return shared_from_this(); }

So when you call getFoo how does exactly it get what is the other shared_ptr to share the ownership with and not to create a separate shared_ptr that owns the same this.

I need to understand this to be able to understand how to create shared_ptr from some object that all increase the same ref count and not initialize separate shared_ptrs.

Narek
  • 38,779
  • 79
  • 233
  • 389
  • 2
    http://en.cppreference.com/w/cpp/memory/enable_shared_from_this Have a look at the notes – StoryTeller - Unslander Monica Dec 03 '15 at 08:58
  • I have seen the Notes that describe the common implementation. Before that, also I have looked at the source code too. But could not understand how this `weak_ptr` is being initialized when first shared_ptr is bing created outside of the class. A class cannot know that we have encapsulated it's pointer in some `shared_ptr`. – Narek Dec 03 '15 at 09:07
  • 4
    You should also have a look at the source of `std::shared_ptr`. The note clearly specifies that there is code there do detect the presence of `std::enable_shared_from_this` as a base class. – StoryTeller - Unslander Monica Dec 03 '15 at 09:09

1 Answers1

42

enable_shared_from_this<T> has a weak_ptr<T> data member. The shared_ptr<T> constructor can detect if T is derived from enable_shared_from_this<T>. If it is, the shared_ptr<T> constructor will assign *this (which is the shared_ptr<T>) to the weak_ptr data member in enable_shared_from_this<T>. shared_from_this() can then create a shared_ptr<T> from the weak_ptr<T>.

Example of a possible implementation:

template<class D>
class enable_shared_from_this {
protected:
    constexpr enable_shared_from_this() { }
    enable_shared_from_this(enable_shared_from_this const&) { }
    enable_shared_from_this& operator=(enable_shared_from_this const&) {
        return *this;
    }

public:
    shared_ptr<T> shared_from_this() { return self_.lock(); }
    shared_ptr<T const> shared_from_this() const { return self_.lock(); }

private:
    weak_ptr<D> self_;

    friend shared_ptr<D>;
};

template<typename T>
shared_ptr<T>::shared_ptr(T* ptr) {
    // ...
    // Code that creates control block goes here.
    // ...

    // NOTE: This if check is pseudo-code. Won't compile. There's a few
    // issues not being taken in to account that would make this example
    // rather noisy.
    if (is_base_of<enable_shared_from_this<T>, T>::value) {
        enable_shared_from_this<T>& base = *ptr;
        base.self_ = *this;
    }
}
Simple
  • 13,992
  • 2
  • 47
  • 47
  • How `shared_ptr` has access to `weak_ptr` member (I guess it is private) of `enable_shared_from_this`. – Narek Dec 03 '15 at 09:32
  • 7
    @Narek it's probably a `friend`. – Simple Dec 03 '15 at 09:35
  • 3
    In C++17, you can make the `if` check (which is currently *pseudo-code*) compilable by using compile-time branch as: `if constexpr( ... ) { ... }` – Nawaz Nov 12 '17 at 16:32
  • Why is it valid to use the template class `enable_shared_from_this` in the copy constructor & assignment without specifying the type argument `D`? – Matěj Kripner Mar 27 '20 at 11:22
  • @MatějKripner because it is in class template body. See Current instantiation in https://en.cppreference.com/w/cpp/language/dependent_name – nullas Jan 15 '21 at 21:49
  • @Simple *The shared_ptr constructor can detect if T is derived from enable_shared_from_this*.How to understand that in the right way? Do you mean not all derived classes can detect itself is derived from a base class? – John Jun 25 '22 at 14:03
  • I think there is a typo. It should read shared_ptr shared_from_this() { return self_.lock(); } and not shared_ptr shared_from_this() { return self_.lock(); } – ZeroCool Jul 02 '22 at 07:30