Как я могу удалить элементы std :: map с помощью итератора? - PullRequest
55 голосов
/ 05 января 2011

Я бы хотел просмотреть std::map и удалить элементы на основе их содержимого.Как лучше всего это сделать?

Ответы [ 3 ]

96 голосов
/ 05 января 2011

Если у вас есть C ++ 11-совместимый компилятор, вот простой способ сделать это:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       itr = myMap.erase(itr);
    } else {
       ++itr;
    }
}

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

Если у вас нет компилятора, совместимого с C ++ 11, или вы работаете со старой кодовой базой, все немного сложнее. До C ++ 11 функция-член erase не возвращала итератор для следующего элемента в карте. Это означало, что для удаления элемента во время итерации вам нужно использовать танец из трех частей:

  1. Скопировать текущий итератор.
  2. Переход текущего итератора к следующему элементу.
  3. Вызовите erase на копии старого итератора.

Это показано здесь:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       std::map<K, V>::iterator toErase = itr;
       ++itr;
       myMap.erase(toErase);
    } else {
       ++itr;
    }
}

Этот процесс был необходим, потому что если бы вы просто вызвали erase на итераторе, вы аннулировали бы это, означая, что такие операции, как увеличение и уменьшение, привели бы к неопределенному поведению. Приведенный выше код позволяет обойти эту проблему, настроив копию итератора, добавив itr, чтобы он находился на следующем элементе, а затем удалив временную копию итератора.

Используя некоторую Умную Уловку, можно уменьшить этот код за счет читабельности. Следующий шаблон распространен в старом коде C ++, но не является обязательным в C ++ 11:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       myMap.erase(itr++);  // <--- Note the post-increment!
    } else {
       ++itr;
    }
}

Использование оператора постинкремента здесь - это умный способ сделать копию старого итератора (помните, что оператор postfix ++ возвращает копию исходного значения итератора), одновременно продвигая более старый итератор.

8 голосов
/ 05 января 2011
for(MyMap::iterator it = mymap.begin(); it!=mymap.end(); ) {
  if(mycondition(it))
    it = mymap.erase(it);
  else
    it++;
}

edit: кажется, что это работает только в MSVC

edit2: в c ++ 0x это работает и для ассоциативных контейнеров

7 голосов
/ 05 января 2011


Это один простой способ:

    int value_to_delete( 2 );
    for( std::map<int, int>::iterator i = mm.begin(); i != mm.end(); ) {
        if( i->second != value_to_delete ) {
            mm.erase( i++ ); // advance before iterator become invalid
        }
        else {
            ++i;
        }
    }
...