0

I understand that most std library implementations choose to implement std::enable_shared_from_this by storing a std::weak_ptr in the base class. This leads to the following:

#include <memory>

class Foo : public std::enable_shared_from_this<Foo>
{
public:
    std::shared_ptr<Foo> GetSharedBar() const
    {
        // returns shared_ptr<const Foo> instead of std::shared_ptr<Foo>
        return shared_from_this();
        // Error in VC++2019 due to mismatch.
    }
};

It seems clear to me that despite needing to update the reference count, shared_from_this() is not actually mutating the target object. This seems like an ideal use for mutable, so that shared_from_this() can be marked as const with respect to the derived object.

Why isn't it implemented with mutable?

To elucidate the answer below: it is implemented with the std::weak_ptr marked mutable, but that only allows the std::weak_ptr to be mutated, not for us to hand off a non-const to someone else.

Joshua W
  • 1,103
  • 12
  • 29
  • @Eljay, are you using VC++? I updated the sample to include everything necessary to reproduce my observation. – Joshua W Oct 08 '20 at 05:01
  • Surely this behaviour is correct though? In `GetSharedBar()`, `this` is a pointer to a `const` `Foo`. You can't possibly return it as pointer to a non-`const` `Foo` (wrapped in a `shared_ptr<>` or otherwise) without some casting shenanigans. – Ken Wayne VanderLinde Oct 08 '20 at 05:45
  • Well my understanding is that std::enable_shared_from_this constains a std::weak_ref pointing at this, not const this. If the weak_ref is marked mutable, would we not be able to then instantiate a std::shared_ptr pointing at this using the copy constructor? – Joshua W Oct 08 '20 at 05:50
  • I think it's working as intended, if you take out the shared pointer and just attempt to return `this` as a non-const pointer or reference you'll get the same error – Alan Birtles Oct 08 '20 at 06:41
  • *Why isn't it implemented with mutable?* The control block **is** implemented with mutable. The problem is either the method needs to be non-const, or the return type needs to be `std::shared_ptr`, or you need to `return const_cast(this)->shared_from_this()` which is janky. – Eljay Oct 08 '20 at 14:02
  • @Eljay In almost every STL container, the elements are "implemented with mutable" by your def? (Although of course the public interface of STL containers apply deep constness on elements.) – curiousguy Dec 04 '20 at 22:41
  • @curiousguy • The shared_ptr's **control block**. Not the elements. – Eljay Dec 04 '20 at 23:12
  • @Eljay Again: by your own definition, are the elements of a container "implemented with mutable"? – curiousguy Dec 05 '20 at 02:49
  • @curiousguy • The elements of a container are not implemented with mutable. That is not my definition. For a container that holds `const` elements, the `const` elements are immutable (at least logically, C++ provides means to circumvent that, possibly leading to UB) unless members are marked mutable. For a `shared_ptr`, the **control block** is mutable, so even a `const shared_ptr` can make more shared pointers. – Eljay Dec 05 '20 at 13:52

1 Answers1

2

These both fail to compile:

class Foo : public std::enable_shared_from_this<Foo>
{
public:
    std::shared_ptr<Foo> GetSharedBar() const
    {
        // error: could not convert from 'shared_ptr<const Foo>' to 'shared_ptr<Foo>'
        return shared_from_this();
    }
};

class Bla
{
public:
    Bla* getThis() const
    {
        // error: invalid conversion from 'const Bla*' to 'Bla*'
        return this;
    }
};

But they both work if you remove const from the functions. The issue is that in a const member function, the this pointer is a const pointer.

As another example, take this:

class kluf
{
    const std::string* k;
    std::string* getK() 
    {
        // error: invalid conversion from 'const string*' to 'std::string*'
        return k;
    }
};

Clearly you are not allowed to hand a const member off to somebody else in a non-const state. This also goes for the this pointer, and in a const function this is const.

Frodyne
  • 3,547
  • 6
  • 16
  • Thanks, your second to last sentence by itself did the trick for me thinking about this! Problem isn't one of the `std::weak_ptr` being mutated, but rather handing off a non-const as a return value to someone else. – Joshua W Oct 09 '20 at 03:27