Использование erase-remove_if идиома

Допустим, у меня есть std::vector<std::pair<int,Direction>>,

Я пытаюсь использовать erase-remove_if идиома для удаления пар из вектора.

stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; }));

Я хочу удалить все пары, для которых значение .first установлено в 4.

В моем примере у меня есть пары:

- 4, Up
- 4, Down
- 2, Up
- 6, Up

Однако после того, как я выполню erase-remove_if, у меня останется:

- 2, Up
- 6, Up
- 6, Up

Что я здесь не так делаю?

6

Решение

Правильный код:

stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool
{ return stopPoint.first == 4; }),
stopPoints.end());

Вы должны удалить диапазон, начиная с итератора, возвращенного из std::remove_if до конца вектора, а не только один элемент.

«Зачем?»

  • std::remove_if меняет местами элементы внутри вектора, чтобы поместить все элементы, которые не соответствуют предикату в начале контейнера.

    • Тогда возвращает итератор, который указывает на первый элемент, соответствующий предикату.

    • std::vector::erase нуждается в стереть диапазон, начиная с возвращенного итератора и заканчивая вектором, чтобы удалить все элементы, которые соответствуют предикату.


Дополнительная информация: Стереть-удалить идиому (Википедия).

14

Другие решения

Метод std::vector::erase имеет две перегрузки:

iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );

Первый только удалить элемент в pos в то время как второй удалить диапазон [first, last),

Так как вы забыли last итератор в вашем вызове, первая версия выбирается по разрешению перегрузки, и вы удаляете только первую пару, сдвинутую в конец на std::remove_if, Вам нужно сделать это:

stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; }),
stopPoints.end());

стереть-удалить идиома работает следующим образом: допустим, у вас есть вектор {2, 4, 3, 6, 4} и вы хотите удалить 4:

std::vector<int> vec{2, 4, 3, 6, 4};
auto it = std::remove(vec.begin(), vec.end(), 4);

Преобразует вектор в {2, 3, 6, A, B} поместив «удаленные» значения в конце (значения A а также B в конце не указано (как если бы значение было переехал), поэтому вы получили 6 в вашем примере) и вернуть итератор A (первое из «удаленных» значений).

Если вы делаете:

vec.erase(it)

Первая перегрузка std::vector::erase выбран, и вы удаляете только значение в it, какой A и получить {2, 3, 6, B},

Добавляя второй аргумент:

vec.erase(it, vec.end())

Выбрана вторая перегрузка, и вы стираете значение между it а также vec.end()так что оба A а также B стерты.

7

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector