Удаление или удаление содержимого с карты (или любого другого контейнера STL) при повторном ее выполнении

Как утверждается, вы не можете просто удалить или удалить элемент в контейнере, пока итерация по мере того, как итератор становится недействительным. Каковы (безопасные) способы удаления элементов, удовлетворяющих определенному условию? пожалуйста, только stl, no boost или tr1.

ИЗМЕНИТЬ Есть ли более элегантный способ, если я хочу удалить несколько элементов, которые отвечают определенным критериям, возможно, используя алгоритм functor и for_each или erase?

Ответ 1

bool IsOdd( int i )
{
    return (i&1)!=0;
}

int a[] = {1,2,3,4,5};
vector<int> v( a, a + 5 );
v.erase( remove_if( v.begin(), v.end(), bind1st( equal_to<int>(), 4 ) ), v.end() );
// v contains {1,2,3,5}
v.erase( remove_if( v.begin(), v.end(), IsOdd ), v.end() );
// v contains {2}

Ответ 2

Вы можете до тех пор, пока вы не сделаете недействительным ваш итератор после его удаления:

MyContainer::iterator it = myContainer.begin();
while(it != myContainer.end())
{
    if (*it == matchingValue)
    {
       myContainer.erase(it++);
    }
    else
    {
        ++it;
    }
}

Ответ 3

Пример с std::vector

#include <vector>

using namespace std;

int main()
{

   typedef vector <int> int_vector;

   int_vector v(10);

   // Fill as: 0,1,2,0,1,2 etc
   for (size_t i = 0; i < v.size(); ++i){
      v[i] = i % 3;
   }

   // Remove every element where value == 1    
   for (int_vector::iterator it = v.begin(); it != v.end(); /* BLANK */){
      if (*it == 1){
         it = v.erase(it);
      } else {
         ++it;
      }
   }

}

Ответ 4

Решение Viktor имеет потенциал для того, чтобы что-то сделать с элементом перед удалением. (Я не смог сделать это с помощью remove_if или remove_copy_if.) Но я предпочитаю использовать std::find_if, поэтому мне не нужно увеличивать итератор самостоятельно:

typedef vector<int> int_vector;
int_vector v;

int_vector::iterator itr = v.begin();
for(;;)
{
    itr = std::find_if(itr, v.end(), Predicate(4));
    if (itr == v.end())
    {
        break;
    }

    // do stuff with *itr here

    itr = v.erase(itr);  // grab a new, valid iterator
}

Где Predicate может быть bind1st( equal_to<int>(), 4 ) или что-то вроде этого:

struct Predicate : public unary_function<int, bool>
{
    int mExpected;
    Predicate(int desired) : mExpected(desired) {}
    bool operator() (int input)
    {
        return ( input == mExpected );
    }
};

Ответ 5

template <class Container, class Predicate>
void eraseIf( Container& container, Predicate predicate  ) {
    container.erase( remove_if( container.begin(), container.end(), predicate ), container.end() );
}   

template<class K, class V, class Predicate> 
void eraseIf( map<K,V>& container, Predicate predicate  ) { 
    for(typename map<K,V>::iterator iter=container.begin() ; iter!=container.end() ; ++iter )   {
        if(predicate(iter))
            container.erase(iter);
    }
}

Ответ 6

Я предпочитаю версию с while:

typedef std::list<some_class_t> list_t;
void f( void ) {
  // Remove items from list
  list_t::iterator it = sample_list.begin();
  while ( it != sample_list.end() ) {
    if ( it->condition == true ) {
      it = sample_list.erase( it );
    } else ++it;    
  }
}

С while нет опасности для увеличения it в два раза, так как это может быть в цикле for.

Ответ 7

markh44 - это самый STL-ish-ответ. Однако обратите внимание, что в целом итераторы недействительны путем изменения контейнера, но набор и отображение являются исключениями. Там вы можете удалить элементы и продолжать использовать итераторы, за исключением случаев, когда вы удаляете тот самый объект, на который ссылается итератор.

Ответ 8

Используйте тот факт, что оператор пост-декремента возвращает копию итератора до того, как он уменьшится. Поскольку уменьшенный итератор остается в силе после стирания текущего элемента, цикл цикла продолжает работать, как предполагалось.

#include <list>
std::list<int> myList;
for(int i = 0; i < 10; ++i )
{
   myList.push_back(i);
}

int cnt = 0;
for(std::list<int>::iterator iter = myList.begin(); iter != myList.end(); ++iter)
{
   if( cnt == 5 )
   {
      myList.erase(iter--);
   }
   ++cnt;
}

Изменить: не работает, если вы попытаетесь удалить первый элемент в списке....

Ответ 9

1.For std::vector<>:

  std::vector <int> vec;
  vec.erase(std::remove(vec.begin(),vec.end(), elem_to_remove)vec.end());

2.For std::map<> всегда используйте std::map::erase()

std::map<int,std::string> myMap;
myMap.emplace(std::make_pair(1, "Hello"));
myMap.emplace(std::make_pair(2, "Hi"));
myMap.emplace(std::make_pair(3, "How"));
myMap.erase( 1);//Erase with key
myMap.erase(myMap.begin(), ++myMap.begin() );//Erase with range
 for( auto &ele: myMap)
{
    if(ele.first ==1)
    {
        myMap.erase(ele.first);//erase by key 
        break;//You can't use this ele again properly 
              //wthin this iteration,   so break.
    }
}
  1. Для std::list используйте std::list::erase()