简体   繁体   中英

How can I use std::remove on a container with std::tr1::weak_ptr?

If I had a STL container, say a list of pointers I could remove them like in the exmple below. With a container of weak_ptrs this does not work, because they cannot be compared, as they need to be locked first. What can I do?

void MyClass::RemoveItem(std::tr1::weak_ptr<Item> const & pItem)
{
    mylist.remove(pItem);
}

For one thing, you could just define operator == for any weak_ptr. I'm sure there's a reason this is not implemented, it can probably bite you at a later point.

template <typename T>
bool operator == (const std::tr1::weak_ptr<T>& a, const std::tr1::weak_ptr<T>& b)
{
    return a.lock() == b.lock();
}

... and you'll be able to just call remove() as usual. This is a little bit extreme I guess.

If you stick to the remove_if() approach, you could get rid of the bind magic* by using function object:

struct EqPredicate
{
    const boost::weak_ptr<Item>& theItem;

    EqPredicate(const boost::weak_ptr<Item>& item) : theItem(item) 
    {
    }

    bool operator () (const boost::weak_ptr<Item>& p) const 
    { 
        return p.lock() == theItem.lock(); 
    }
};

and then use it like this:

mylist.remove_if(EqPredicate(pItem));

It looks like more code, but you can compress the EqPredicate class, it's mostly hollow. Also, it could be made template to use it with lists containing types other than Item.

Oh, and do pass you weak_ptrs by reference everywhere including your comparison function.

*bind is not free performance-wise. If you expect a lot of Remove() calls and care much about performance it might be good to avoid it.

I think the problem with sbk's approach is the weak_ptr operator== has the potential for a race. There's no guarantee of a shared_ptr to a or b existing even as you return from operator==, which makes it easy to misinterpret the resulting code.

With it, it seems the best you could do is:

if(a == b) {
  boost::shared_ptr<Item> a_locked(a.lock());
  boost::shared_ptr<Item> b_locked(b.lock());
  // It is an error to assume a_locked == b_locked here
  // It is an error to assume a.lock() == b.lock() here
  // It is an error to assume a.get() or b.get() here
}

which is not that useful. Now, if you were iterating over a container, you could still remove the iterators at this point, but there are many more cases where you would have ended up making just slightly the wrong comparison.

Just because I searched like forever to find the answer.

Create a function to compare the weak_ptrs and then bind one argument.

    bool weak_ptr_comparsion(Item::wPtr  a, Item::wPtr  b)
    {
        return a.lock() == b.lock();
    }

    void MyClass::RemoveItem(Item::wPtr const & pItem)
    {
        mylist.remove_if(std::tr1::bind(weak_ptr_comparsion, pItem, 
                         std::tr1::placeholders::_1));
    }

Dont forget to include <tr1/functional>

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM