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 to count/find
the weak_ptr<int>
stored in intset
and if a better method is available to do the same work?
You cannot insert temporary shared_ptr to set of weak pointers because it is 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
That is why you cannot find it in set - because *lhs.lock()
is UB here.
See weak_ptr::lock doc .
You need to make your òrder operator in this way:
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 that means - you need to have this shared_ptr sowmewhere to count 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 count will work.
Consider also to keep shared_ptr in set...
[UPDATE]
marko in comments pointed the valid issue. std::weak_ptr cannot be used as a key in a way you are using it at all. Only if you can ensure that pointed value will never change nor 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 the other 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 keep std::shared_ptr which prevents from out-of-set expiration of pointer - and std::shared_ptr has operator <
- but this operator compares pointers themselves - not the pointed values - so better is 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 with 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;
});
shared_ptr
does NOT implement flyweight pattern, which you seem to assume.
make_shared
returns a pointer that is capable of being shared. To get more pointers to the same object, you must use copy constructor or copy assignment operator, and pass the existing shared_ptr
.
make_shared
does NOT make an additional pointer to an existing object. It creates a new object.
Therefore, it is correct that intset.count(make_shared<int>(1))
returns 0
. The just-created shared_ptr<int>
does not exist anywhere in the set.
Successive calls to make_shared<int>(1)
do not compare equal.
Then there's the additional brokenness associated with your comparison function. There is an ordering that works for weak_ptr
, but that is not it. Your unstable comparison function will cause set
to badly misbehave.
You should use simply set<int>
.
我们可以使用标准的std::owner_less<>
代替尝试为弱指针编写自己的比较函数并提出行为不端的解决方案。
It's a bad idea to have a set of weak pointers unless you implement a cleanup method and use it regularly. You might want to put a safeguard in your comparison function because at present the result is undefined. Taking the 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());
}
};
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(); }
);
}
You can use a static shared pointer to store the query (although this is kind of 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";
}
wrap up the weak_pointer in a 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 as:
std::set<WeakPtrAsKey<MyKey>> mySet;
std::map<WeakPtrAsKey<MyKey>,MyVal> myMap;
usage examples:
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();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.