СТД:: enable_shared_from_this; общественный против частного

Я немного поиграл с использованием shared_ptr и enable_shared_from_this, в то время как я сталкивался с тем, чего я действительно не понимаю.

В моей первой попытке я построил что-то вроде этого:

class shared_test : std::enable_shared_from_this<shared_test> {
public:
    void print(bool recursive) {
        if (recursive) {
            shared_from_this()->print(false);
        }

        std::cout << "printing" << std::endl;
    }
};

Обратите внимание, что этот класс расширяет std:: enable_shared_from_this в частном порядке. Это, по-видимому, имеет большое значение, поскольку выполняется что-то вроде этого:

int main() {
    auto t(std::make_shared<shared_test>());
    t->print(true);
    return 0;
}

генерирует исключение bad_weak_ptr. Где, как если бы я изменил определение класса, чтобы оно было открыто публично из std:: enable_shared_from_this, этот пробег просто находит.

Почему, что мне здесь не хватает? И нет ли способа заставить его работать для частного наследования, поскольку "внешний мир" класса shared_test не должен знать, что он разрешает совместное использование этого... (по крайней мере, если вы не спросите меня, или я что-то пропущу?)

Ответ 1

Почему, что мне здесь не хватает?

Чтобы сделать shared_from_this работа enable_shared_from_this должна знать о shared_ptr, которая содержит класс. В вашей реализации STL это weak_ptr, возможны другие реализации. Когда вы наследуете конфиденциально, тогда невозможно получить доступ к свойствам базового класса извне вашего класса. На самом деле даже невозможно понять, что вы унаследовали. Таким образом, make_shared генерирует обычную инициализацию shared_ptr без установки правильных полей в enable_shared_from_this.

Исключение выбрано не из make_shared, а form shared_from_this, потому что enable_shared_from_this не был правильно инициализирован.

И нет ли способа заставить его работать для частного наследования, поскольку "внешний мир" класса shared_test не должен знать, что он разрешает совместное использование этого...

Нет. Внешний мир должен знать, что объект имеет особые отношения с shared_ptr для правильной работы с ним.

Ответ 2

не существует способа заставить его работать для частного наследования, так как "внешний мир" класса shared_test не должен знать, что он разрешает совместное использование этого

shared_ptr сам является частью "внешнего мира"; конструктор shared_ptr должен иметь возможность доступа к подобъекту базового класса enable_shared_from_this объекта shared_test, на который он указывает, для инициализации частного weak_ptr члена реализации enable_shared_from_this.

Ответ 3

Основываясь на документации, необходимо публично наследовать доступ к функции членства "shared_from_this".

"Публично наследование из std:: enable_shared_from_this предоставляет тип T с функцией-членом shared_from_this" - из ссылки CPP http://en.cppreference.com/w/cpp/memory/enable_shared_from_this

shared_from_this:

возвращает shared_ptr, который имеет право владения * этим (функция открытого участника)

Ответ 4

Я анализирую этот вопрос из кода в STL:

auto t (std :: make_shared());

строка кода создает shared_ptr; сначала мы погрузимся в функцию make_shared

 // FUNCTION TEMPLATE make_shared
   template<class _Ty,
   class... _Types>
   NODISCARD inline shared_ptr<_Ty> make_shared(_Types&&... _Args)
   {    // make a shared_ptr
     const auto _Rx = new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);

     shared_ptr<_Ty> _Ret;
    _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Getptr(), _Rx);
     return (_Ret);
   }

Внимание: мы погрузимся в функцию _Ret.Set_ptr_rep_and_enable_shared.И мы можем видеть следующее:

template<class _Ux>
   void _Set_ptr_rep_and_enable_shared(_Ux * _Px, _Ref_count_base * _Rx)
   {    // take ownership of _Px
        this->_Set_ptr_rep(_Px, _Rx);
        _Enable_shared_from_this(*this, _Px);
   }

Итак, мы находим функцию _Enable_shared_from_this, далее:

 template<class _Other,
    class _Yty>
    void _Enable_shared_from_this(const shared_ptr<_Other>& _This, _Yty * _Ptr)
    {   // possibly enable shared_from_this
    _Enable_shared_from_this1(_This, _Ptr, _Conjunction_t<
        negation<is_array<_Other>>,
        negation<is_volatile<_Yty>>,
        _Can_enable_shared<_Yty>>{});
   }

Мы находим ключевую точку: _Can_enable_shared <_Yty>

template<class _Yty,
    class = void>
    struct _Can_enable_shared
        : false_type
    {   // detect unambiguous and accessible inheritance from enable_shared_from_this
    };

template<class _Yty>
    struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>
        : is_convertible<remove_cv_t<_Yty> *, typename _Yty::_Esft_type *>::type
    {   // is_convertible is necessary to verify unambiguous inheritance
    };

мы находим, что только _Yty имеет _Esft_type, а _Yty может быть преобразовано в _Esft_type, может _Yty может быть enable_shared (Если вы хотите узнать больше, то, чтобы увидеть set weak_ptr в _Yty, или вы можете получить ошибку bad_weak_ptr при использовании shared_from_this). Так что же такое _Esft_type?

 template<class _Ty>
    class enable_shared_from_this
    {   // provide member functions that create shared_ptr to this
public:
    using _Esft_type = enable_shared_from_this;
     ...
   }

поэтому _Esft_type означает просто enable_shared_from_this <_Ty>, поэтому, если вы используете приватное наследование, извне не только не будет видно _Esft_type, а _Yt не может быть преобразовано в _Esft_type. Таким образом, weak_ptr не может быть установлен, так что bad_weak_ptr может быть вызван.

Таким образом, извне нужно знать о существовании _Esft_type, поэтому, когда создается shared_ptr, может также быть установлен weak_ptr для shared_test.