Что нужно сделать для физического удаления элементов после std::remove_if
Что проверяют
Понимаете ли вы разделение алгоритмов и контейнеров в стандартной библиотеке. Алгоритм работает с итераторами и физически удалить ничего не может: размер меняет только сам контейнер, и вопрос проверяет именно это знание.
Как отвечать
std::remove_if переставляет элементы так, что оставляемые оказываются в начале диапазона и в исходном порядке, и возвращает итератор на новый логический конец. Элементы за ним находятся в корректном, но неопределённом состоянии: это объекты, из которых значения перенесли. Размер контейнера не меняется, деструкторы не вызываются.
Чтобы удалить физически, возвращённый итератор отдают в erase вместе с концом контейнера:
v.erase(std::remove_if(v.begin(), v.end(),
[](int n) { return n < 5; }),
v.end());Что стоит добавить:
- в современном стандарте есть свободная функция
std::erase_if, которая делает то же самое одной строкой и работает в том числе с ассоциативными контейнерами; - у
std::listиstd::forward_listесть собственный методremove_if: он переставляет указатели узлов, ничего не копирует и сразу уменьшает размер; - к контейнерам с порядком по ключу алгоритм неприменим, менять порядок элементов в них нельзя.
Где спотыкаются
Переворачивают смысл и говорят, что алгоритм сдвигает удаляемые элементы в конец, где их можно посмотреть или пересчитать. В конце лежат объекты, из которых содержимое переместили, обращаться к их значениям нельзя.
Забывают второй итератор и пишут erase от одной позиции. Такой вызов уберёт ровно один элемент, остальной хвост останется на месте, и размер почти не изменится, а тесты поймают это не сразу.
Хранят вектор голых указателей и считают, что объекты удалены. Ни алгоритм, ни erase не освобождают то, на что указатели смотрят: память освобождают до удаления или сразу держат умные указатели.