Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

vector

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());  } 
like image 504
jmasterx Avatar asked Oct 15 '10 01:10

jmasterx


People also ask

How do you delete from a vector while iterating through it?

To delete single element from a vector using erase() function, pass the iterator of element to it like erase(it). It will delete the element pointed by the iterator the it variable. To delete multiple elements from a vector using erase() function, pass the iterator range to it like erase(start, end-1).

How do I remove something from a vector in C++?

The C++ vector has many member functions. Two of these member functions are erase() and pop_back(). pop_back() removes the last element from the vector. In order to remove all the elements from the vector, using pop_back(), the pop_back() function has to be repeated the number of times there are elements.

How do I erase an element from std::vector <> by value?

You need to use std::remove algorithm to move the element to be erased to the end of the vector and then use erase function. Something like: myVector. erase(std::remove(myVector. begin(), myVector.


1 Answers

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.

like image 143
GManNickG Avatar answered Oct 07 '22 19:10

GManNickG