Set weak_ptr

Here is the code:

struct lex_compare { bool operator() (const weak_ptr<int> &lhs, const weak_ptr<int> &rhs)const { return *lhs.lock() < *rhs.lock(); } }; int main(){ set<weak_ptr<int>,lex_compare> intset; intset.insert(make_shared<int>(1)); cout << "intset size:" << intset.size() << endl; //1 cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed } 

I want to know how count/find weak_ptr<int> stored in intset , and if a better method is available to do the same job?

+5
source share
4 answers

You cannot insert a temporary shared_ptr into a set of weak pointers because it is a memory leak in the sense that this stored weak pointer points to already deleted memory.

 intset.insert(make_shared<int>(1)); // after this instruction shared_ptr destructor frees the memory 

This is why you cannot find it in the set, because *lhs.lock() is UB here.

See weak_ptr :: lock doc .

You need to make your Γ²rder statement as follows:

 struct lex_compare { bool operator() (const weak_ptr<int> &lhs, const weak_ptr<int> &rhs)const { auto lptr = lhs.lock(), rptr = rhs.lock(); if (!rptr) return false; // nothing after expired pointer if (!lptr) return true; // every not expired after expired pointer return *lptr < *rptr; } }; 

All this means that you need to have this shared_ptr in order to calculate it:

 int main(){ set<weak_ptr<int>,lex_compare> intset; auto shared1 = make_shared<int>(1); intset.insert(shared1); cout << "intset size:" << intset.size() << endl; //1 cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed } 

With the above - your account will work.

Also consider that shared_ptr in the set ...

[UPDATE]

marko in the comments pointed to a valid problem. std :: weak_ptr cannot be used as a key the way you use it. Only if you can guarantee that the specified value will never change, and the pointer itself will never expire. See this example:

  set<weak_ptr<int>,lex_compare> intset; auto shared1 = make_shared<int>(1); intset.insert(shared1); cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // works shared1.reset(); cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed 

And another example:

  set<weak_ptr<int>,lex_compare> intset; auto shared1 = make_shared<int>(1); intset.insert(shared1); cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // works *shared1 = 2; cout << "Does 1 exist?"<< intset.count(make_shared<int>(1))<<endl; // failed 

You can save std :: shared_ptr, which prevents the pointer from expiring - and std :: shared_ptr has operator < - but this operator compares the pointers themselves and not the default values, so std::set<std::shared_ptr<int>> better std::set<std::shared_ptr<int>> - but the best would be std::set<int>

Or change std::set<...> β†’ std::vector<std::weak_ptr<int>> - and use count_if - see:

 vector<weak_ptr<int>> intset; auto shared1 = make_shared<int>(1); intset.push_back(shared1); cout << "Does 1 exist?"<< count_if(begin(intset), end(intset), [](auto&& elem) { auto ptr = elem.lock(); return ptr && *ptr == 1; }); 

Or using std::set<std::shared_ptr<int>> :

 set<shared_ptr<int>> intset; auto shared1 = make_shared<int>(1); intset.insert(shared1); // if you can ensure shared1 value will not change: cout << "Does 1 exist?"<< intset.count(shared1); // if not - use count_if - the slower than std::count cout << "Does 1 exist?"<< count_if(begin(intset), end(intset), [](auto&& ptr) { return ptr && *ptr == 1; }); 
+3
source

shared_ptr does NOT implement the fly pattern that you seem to assume.

make_shared returns a pointer that can be shared. To get more pointers to the same object, you must use the copy constructor or copy assignment operator and pass the existing shared_ptr .

make_shared does NOT make an extra pointer to an existing object. He creates a new object.

Therefore, it is correct that intset.count(make_shared<int>(1)) returns 0 . The shared_ptr<int> created shared_ptr<int> does not exist anywhere in the set.

Successive calls to make_shared<int>(1) not compared to peers.

Then there is an additional corruption related to your comparison function. There is an order that works for weak_ptr , but it is not. Your unstable comparison function will cause set to work poorly erroneously.

You should just use set<int> .

+1
source

It is a bad idea to have a set of weak pointers unless you implement the cleanup method and use it regularly. You might want to set security in your comparison function, as the result is currently undefined. Taking advice from @PiotrNycz, for example:

 template <class T> struct wptr_less_than { bool operator() ( const std::weak_ptr<T>& lhs, const std::weak_ptr<T>& rhs ) const { return lhs.expired() || (!rhs.expired() && *lhs.lock() < *rhs.lock()); } }; 

Counting valid weak pointers

Use a combination of count_if and weak_ptr::expired :

 template <class T, class C, class A> size_t count_valid_pointers( const std::set< std::weak_ptr<T>, C, A >& s ) { return s.size() - std::count_if( s.begin(), s.end(), []( const std::weak_ptr<T>& wptr ){ return wptr.expired(); } ); } 

Search for an item with a specific value

You can use a static generic pointer to hold the request (although this is pretty ugly):

 template <class T, class C, class A> typename std::set< std::weak_ptr<T>, C, A >::iterator find_value( const std::set< std::weak_ptr<T>, C, A >& s, const T& val ) { static auto query = std::make_shared<T>(); query.reset( const_cast<T*>(&val), []( T* ){} ) ; return s.find(query); } 

And an example:

 #include <algorithm> #include <iostream> #include <memory> #include <set> template <class T> struct wptr_less_than { bool operator() ( const std::weak_ptr<T>& lhs, const std::weak_ptr<T>& rhs ) const { return lhs.expired() || (!rhs.expired() && *lhs.lock() < *rhs.lock()); } }; template <class T, class C, class A> size_t count_valid_pointers( const std::set< std::weak_ptr<T>, C, A >& s ) { return s.size() - std::count_if( s.begin(), s.end(), []( const std::weak_ptr<T>& wptr ){ return wptr.expired(); } ); } template <class T, class C, class A> typename std::set< std::weak_ptr<T>, C, A >::iterator find_value( const std::set< std::weak_ptr<T>, C, A >& s, const T& val ) { static auto query = std::make_shared<T>(); query.reset( const_cast<T*>(&val), []( T* ){} ) ; return s.find(query); } int main() { std::set< std::weak_ptr<int>, wptr_less_than<int> > intset; auto a = std::make_shared<int>(1); auto b = std::make_shared<int>(2); intset.insert(a); intset.insert(b); a.reset(); std::cout << "intset size:" << intset.size() << std::endl; //2 std::cout << "intset real size:" << count_valid_pointers(intset) << std::endl; //1 if ( find_value(intset,2) != intset.end() ) std::cout << "Found it!\n"; } 
0
source

shut down weak_pointer in the container

eg.

 template <class T> class WeakPtrAsKey { T * m_ptr; typedef std::shared_ptr<T> _Sptr; typedef std::weak_ptr<T> _Wptr; _Wptr m_wptr; public: WeakPtrAsKey():m_ptr(nullptr) {}; WeakPtrAsKey(_Wptr wptr):m_ptr(wptr.lock().get()),m_wptr(wptr) {} WeakPtrAsKey(_Sptr sptr):m_ptr(sptr.get()),m_wptr(sptr) {} bool operator<(const WeakPtrAsKey<T> &other) const { return m_ptr<other.m_ptr;} bool operator==(const WeakPtrAsKey<T> &other) const { return m_ptr==other.m_ptr;} _Wptr getWeak() const { return m_wptr;} _Sptr lock() const { return m_wptr.lock();} }; 

and use it like:

 std::set<WeakPtrAsKey<MyKey>> mySet; std::map<WeakPtrAsKey<MyKey>,MyVal> myMap; 

examples of using:

 void addToMap(std::weak_ptr<MyKey> key, const MyVal &val) { myMap[key]=val } void addToMap(std::shared_ptr<MyKey> key, const MyVal &val) { myMap[key]=val } std::shared_ptr<MyKey> getFirstKey() { auto it=myMap.begin(); return=it->first.lock(); } 
0
source

All Articles