I have a std::list
of boost::shared_ptr<T>
and I want to remove an item from it but I only have a pointer of type T* which matches one of the items in the list.
However I cant use myList.remove( tPtr )
I'm guessing because shared_ptr does not implement ==
for its template argument type.
My immediate thought was to try myList.remove( shared_ptr<T>(tPtr) )
which is syntactically cor开发者_运维问答rect but it will crash from a double delete since the temporary shared_ptr
has a separate use_count.
std::list< boost::shared_ptr<T> > myList;
T* tThisPtr = new T(); // This is wrong; only done for example code.
// stand-in for actual code in T using
// T's actual "this" pointer from within T
{
boost::shared_ptr<T> toAdd( tThisPtr ); // typically would be new T()
myList.push_back( toAdd );
}
{
//T has pointer to myList so that upon a certain action,
// it will remove itself romt the list
//myList.remove( tThisPtr); //doesn't compile
myList.remove( boost::shared_ptr<T>(tThisPtr) ); // compiles, but causes
// double delete
}
The only options I see remaining are to use std::find with a custom compare, or to loop through the list brute force and find it myself, but it seems there should be a better way.
Am I missing something obvious, or is this just too non-standard a use to be doing a remove the clean/normal way?
You're correct, we can't directly compare the pointers. But there does exist remove_if
, and we can specify our own predicate. The solution:
template <typename T>
struct ptr_contains_predicate
{
ptr_contains_predicate(T* pPtr) :
mPtr(pPtr)
{}
template <typename P>
bool operator()(const p& pPtr) const
{
return pPtr.get() == mPtr;
}
T* mPtr;
};
template <typename T>
ptr_contains_predicate<T> ptr_contains(T* pPtr)
{
return ptr_contains_predicate<T>(pPtr);
}
Just keep the above predicate in a header somewhere, and you can use it wherever you want.
myList.remove_if(ptr_contains(tThisPtr));
The best solution is to never lose hold of the shared_ptr
in the first place, so we can just use remove
, but the above is harmless anyway.
std::list's remove_if member is what you need:
Define a predicate
template <typename T> struct shared_equals_raw
{
shared_equals_raw(T* raw)
:_raw(raw)
{}
bool operator()(const boost::shared_ptr<T>& ptr) const
{
return (ptr.get()==_raw);
}
private:
T*const _raw;
};
then you can call
myList.remove_if(shared_equals_raw(tThisPtr));
to have the list clean up nodes which have shared_ptrs to tThisPtr.
(Untested, so maybe some syntactic stuff needs fixing).
Michael Burr's advice re enable_shared_from_this is good though; it'd be better to avoid having the raw tThisPtr in play at all.
enable_shared_from_this
can help with your problem, but it will require that the types you're using in the list derive from it:
- http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/enable_shared_from_this.html
If the type enables that functionality, you can get the shared pointer from the object itself by calling shared_from_this()
.
Can you use the shared pointer to remove it?
std::list< boost::shared_ptr<T> > myList;
boost::shared_ptr<T> tThisPtr = new T();
{
myList.push_back(tThisPtr);
}
{
myList.remove(tThisPtr);
}
精彩评论