Erasing from a std::vector while doing a for each?

erase() returns a new iterator:

for(iterator it = begin; it != end(container) /* !!! */;)
{
    if (it->somecondition())
    {
        it = vec.erase(it);  // Returns the new iterator to continue from.
    }
    else
    {
        ++it;
    }
}

Note that we can no longer compare it against a precalculated end, because we may erase it and therefore invalidate it. We must get the end explicitly each time.

A better method might be to combine std::remove_if and erase(). You change from being O(N2) (every element gets erased and shifted as you go) to O(N):

iterator it = std::remove_if(begin, end, pred);
vec.erase(it, vec.end());

Where pred is your removal predicate, such as:

struct predicate // do choose a better name
{
    bool operator()(const T& pX) const // replace T with your type
    {
        return pX.shouldIBeRemoved();
    }
};

iterator it = std::remove_if(begin, end, predicate());
vec.erase(it, vec.end());

In your case, you can make it pretty general:

class remove_by_caller
{
public:
    remove_by_caller(AguiWidgetBase* pWidget) :
    mWidget(pWidget)
    {}

    // if every thing that has getCaller has a base, use that instead
    template <typename T> // for now a template
    bool operator()(const T& pX) const
    {
        return pX.getCaller() == mWidget;
    }

private:
    AguiWidgetBase* mWidget;
};

std::vector<AguiTimedEvent>::iterator it =
    std::remove_if(timedEvents.begin(), timedEvents.end(), remove_by_caller(widget));
timedEvents.erase(it, timedEvents.end());

Note lambda’s exist to simplify this process, both in Boost and C++11.

Leave a Comment