Допустим, у меня есть 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
Что я здесь не так делаю?
Правильный код:
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
нуждается в стереть диапазон, начиная с возвращенного итератора и заканчивая вектором, чтобы удалить все элементы, которые соответствуют предикату.
Дополнительная информация: Стереть-удалить идиому (Википедия).
Метод 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
стерты.