I have a struct, A
, objects of which are managed by shared_ptr
s. Struct A
holds a reference to struct B
. B
objects need to keep track of which A
objects hold references to them, and also need to be able to return shared_ptr
s to these objects. To make this easy, I'm storing a set of weak_ptr
s to associated A
objects inside of B
. So far, so good.
My problem is that I want A
's destructor to remove the reference to itself from its associcated B
object. However, (what I thought was) the obvious solution doesn't work, as by the time A
's destructor is called, its associated weak_ptr
s are expired, making it difficult to delete from the set. Here's what I tried:
#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;
}
What is the correct way to accomplish this? I could just have A
's destructor run through B's set and erase any expired weak_ptrs, but that doesn't seem clean. I could also just convert B
's set to raw A
pointers, and use those raw pointers to access A's shared_from_this()
when needed, but I can't help but think I'm just doing something wrong.
Since you haven't mentioned the compiler - if you are on a new enough compiler, then you could use weak_from_this (available from C++17):
b.set_of_a.erase(weak_from_this());
This would actually achieve what you want in a clean way since then you would just be comparing the actual weak_ptr
instances instead of trying to create a new shared instance in the dtor, which logically fails now. Seems to work on coliru, but did not work on VS2017 (15.4.1) with C++17 enabled.
Update for curiousguy
This snippet:
#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;
}
gives output:
Size of set_of_a: 1
Size of set_of_a: 0
on coliru (gcc 8).