Поиск слабого_потора после истечения срока действия shared_ptr

У меня есть структура, Aобъекты которого управляются shared_ptrs. Struct A содержит ссылку на структуру B, B объекты должны отслеживать, какие A объекты содержат ссылки на них, а также должны иметь возможность вернуть shared_ptrс этими объектами. Чтобы сделать это легко, я храню набор weak_ptrс ассоциированным A объекты внутри B, Все идет нормально.

Моя проблема в том, что я хочу AДеструктор для удаления ссылки на себя из своей привязанной B объект. Однако (то, что я думал, было) очевидное решение не работает, как к тому времени Aдеструктор называется, его связывают weak_ptrсрок действия истек, что затрудняет удаление из набора. Вот что я попробовал:

#include <memory>
#include <set>

struct A;
struct B;

struct B{
   std::set<std::weak_ptr<A>, std::owner_less<std::weak_ptr<A>>> set_of_a;
};

struct A : std::enable_shared_from_this<A>{
   B &b;
   A(B &b):b(b){};
   ~A(){
      // bad_weak_ptr exception here
      b.set_of_a.erase(shared_from_this());
   }
};


int main(){
   B b;
   std::shared_ptr<A> a1 = std::make_shared<A>(b);
   b.set_of_a.insert(a1);
   {
      std::shared_ptr<A> a2 = std::make_shared<A>(b);
      b.set_of_a.insert(a2);
   }
   return 0;
}

Как правильно это сделать? Я мог бы просто иметь Aдеструктор запускает набор B и удаляет все просроченные версии файлаугирых, но это не кажется чистым. Я также мог бы просто конвертировать Bнастроен на сырье A указатели и использовать эти необработанные указатели для доступа к shared_from_this() когда нужно, но я не могу не думать, что просто делаю что-то не так.

2 ответа

Решение

Поскольку вы не упомянули компилятор - если вы используете достаточно новый компилятор, вы можете использовать weak_from_this (доступно из C++17):

b.set_of_a.erase(weak_from_this());

Это на самом деле достигнет того, что вы хотите, чистым способом, так как тогда вы просто будете сравнивать фактические weak_ptr экземпляры вместо того, чтобы пытаться создать новый общий экземпляр в dtor, который теперь логически не работает. Кажется, работает на coliru, но не работает на VS2017 (15.4.1) с включенным C++ 17.

Обновление для любопытных

Этот фрагмент:

#include <memory>
#include <set>
#include <iostream>

struct A;
struct B;

struct B{
   std::set<std::weak_ptr<A>, std::owner_less<std::weak_ptr<A>>> set_of_a;
};

struct A : std::enable_shared_from_this<A>{
   B &b;
   A(B &b):b(b){};
   ~A(){
      b.set_of_a.erase(weak_from_this());
      std::cout << "Size of set_of_a: " << b.set_of_a.size() << "\n";
   }
};


int main(){
   B b;
   std::shared_ptr<A> a1 = std::make_shared<A>(b);
   b.set_of_a.insert(a1);
   {
      std::shared_ptr<A> a2 = std::make_shared<A>(b);
      b.set_of_a.insert(a2);
   }
   return 0;
}

дает вывод:

Size of set_of_a: 1
Size of set_of_a: 0

на колиру (гкц 8).

Не беспокойтесь, стирая элементы из B::set_of_a, Когда ты lock weak_ptr чтобы получить доступ к объекту, проверьте, является ли он нулевым, и удалите его.

Ты не можешь shared_from_this после ~A началось, A перестало быть.

Другие вопросы по тегам