Trivial cases of shared_ptr and weak_ptr failing
Asked Answered
I

2

32

I'm having trouble using shared_ptr and weak_ptr along with enable_shared_from_this.

When I google the symptoms of what I'm seeing, everybody suggests "you cannot use shared_from_this() when there are no shared_ptr instances owning your object.

But that's not my case.

Consider this code:

#include <memory>
#include <cassert>

class MyClass : std::enable_shared_from_this<MyClass>
{
public:
    void this_fails()
    {
        // Doesn't even assert(), because it throws bad_weak_ptr
        assert(shared_from_this());
    }
    void this_fails_too()
    {
        std::weak_ptr<MyClass> weak = weak_from_this();
        std::shared_ptr<MyClass> strong = weak.lock();
        // This assert fails
        assert(strong.get());
    }
};

int main()
{
    std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();

    obj->this_fails();
    obj->this_fails_too();
}

Both methods in MyClass crash the program. I must be missing something obvious - what is it?

Insignia answered 16/7, 2019 at 19:33 Comment(0)
C
42

You must inherit publicly from std::enable_shared_from_this. Inheriting privately doesn't help - std::shared_ptr can't access the base class and set it up properly.

Carolyn answered 16/7, 2019 at 19:38 Comment(7)
That must be the one of the most counter-intuitive behaviors I have seen. Why doesn't it cause compile time errors? -- Explanation is here: #39937612Insignia
@Insignia I don't see why you would expect a compiler error. From the compilers point of view, inheriting privately is fine and calling shared_from_this is fine. That it doesn't give the desired run-time behaviour is not the compilers problem. The code is well formed C++ regardless of whether it gives you the expected behaviour or not.Muncey
@Insignia There is no easy way to give a compile error. The std::shared_ptr constructor has no mechanism to tell whether you made a mistake by privately inheriting from std::enable_shared_from_this or whether you just privately inherited from some class that publicly inherited from std::enable_shared_from_this. The shared_from_this function has no way of knowing the most derived class type so it also cannot detect the programmer's error.Rossman
Well, the standard could mandate checking for base with std::is_base_of, and then complaining if it is ambiguous or inaccessible.Thrust
@Insignia What's counter intuitive is that a class isn't required to derive from std::enable_shared_from_this but something will occur only if it does. This is very dirty and will not work if you allocate another class that uses membership instead of inheritance. Just have your own weak_ptr and maintain it yourself.Ferree
@Thrust But then you will have problems if you try to privately inherit from a class that has inherited from std::enable_shared_from_this. Though, maybe this use case is rare enough to not matter.Rossman
@Brian That you will have anyway. Considering the private base depends on the Management being saved.Thrust
C
17

You have to publicly inherit from std::enable_shared_from_this in order for it to work.

Cobbs answered 16/7, 2019 at 19:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.