Я хотел бы выполнить цикл std::map
и удалить элементы на основе их содержимого. Как это лучше всего сделать?
Как я могу удалить элементы std:: map с помощью итератора?
Ответ 1
Если у вас есть компилятор, совместимый с С++ 11, вот простой способ сделать это:
std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
if (ShouldDelete(*itr)) {
itr = myMap.erase(itr);
} else {
++itr;
}
}
Идея состоит в том, чтобы переместить итератор вперед от начала контейнера до конца, проверяя на каждом шаге, должна ли быть удалена текущая пара ключей/значений. Если это так, мы удалим элемент, итератированный с помощью функции члена erase
, который затем возвращает итератор в следующий элемент на карте. В противном случае мы заранее продвигаем итератор вперед.
Если у вас нет компилятора, совместимого с С++ 11, или вы работаете со старой кодовой базой, все немного сложнее. До С++ 11 функция-член erase
не возвращала итератор в следующий элемент на карте. Это означало, что для удаления элемента во время итерации вам нужно будет использовать танцы из трех частей:
- Скопировать текущий итератор.
- Перенесите текущий итератор на следующий элемент.
- Вызов
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
на итераторе, вы сделали бы его недействительным, что означает, что операции, такие как приращение и декремент, приведут к поведению undefined. Вышеприведенный код обойдется в этом, установив копию итератора, продвигая itr
, чтобы он появился в следующем элементе, а затем удалил временную копию итератора.
Используя некоторую умную хитрость, можно уменьшить этот код за счет удобочитаемости. Следующий шаблон распространен в старшем С++-коде, но в С++ 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;
}
}
Использование оператора post-increment - это умный способ сделать копию старого итератора (помните, что оператор postfix ++ возвращает копию исходного значения итератора), а также продвигая старый итератор.
Ответ 2
Это простой способ:
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;
}
}
Ответ 3
for(MyMap::iterator it = mymap.begin(); it!=mymap.end(); ) {
if(mycondition(it))
it = mymap.erase(it);
else
it++;
}
edit: похоже, что это работает только в MSVC
edit2: в С++ 0x это тоже работает для ассоциативных контейнеров