Erasing from a std::vector while doing a for each?
The proper way to iterate is to use iterators. However, I think by erasing, the iterator is invalidated.
Basically what I want to do is:
for(iterator it = begin; it != end; ++it)
{
if(it->somecondition() )
{
erase it
}
}
How could I do this without v[i] method?
Thanks
struct RemoveTimedEvent
{
bool operator()(const AguiTimedEvent& pX, AguiWidgetBase* widget) const
{
return pX.getCaller() == widget;
}
};
void AguiWidgetContainer::clearTimedEvents( AguiWidgetBase* widget )
{
std::vector<AguiTimedEvent>::iterator it = std::remove_if(timedEvents.begin(),
timedEvents.end(), RemoveTimedEvent());
timedEvents.erase(it, timedEvents.end());
}
Solution 1:
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.