Идиома remove-erase idiom призвана решить проблему удаления элементов из контейнера, поскольку данная проблема может представлять нетривиальную задачу, чреватую возникновением ошибок. Данная идиома предполагает применение алгоритма remove() или remove_if(), за которым следует вызов функции erase() контейнера.
При применении алгоритмов remove() и remove_if() те элементы, которые надо сохранить, помещаются в начало контейнера, а функции remove()
и remove_if()
возвращают итератор на первый удаляемый элемент. Затем этот итератор передается в функцию erase()
, которая собственно и удаляет элементы.
Реализация идиомы:
#include <iostream> #include <vector> #include <algorithm> void print (const std::vector<int>& data) { for(const auto& n: data) { std::cout << n << "\t"; } std::cout << std::endl; } bool is_negative(int n){ return n < 0;} int main() { std::vector<int> numbers {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5}; // применяем алгоритм remove_if() для удаления всех элементов, которые не соответствуют условию auto iter{ std::remove_if(begin(numbers), end(numbers), is_negative) }; print(numbers); // 0 1 2 3 4 5 1 2 3 4 5 // удаляем все элементы, начиная с итератора first_to_erase numbers.erase(iter, end(numbers)); print(numbers); // 0 1 2 3 4 5 }
Здесь для примера удаляем из вектора все отрицательные числа. Для этого сначала вызываем функцию std::remove_if()
:
auto iter{ std::remove_if(begin(numbers), end(numbers), is_negative) }; print(numbers);
В качестве первого и второго параметров она принимает итераторы на начало и конец диапазона, из которого надо удалить числа. Здесь диапазон определяется итераторами на начало и конец
вектора. В качестве третьего параметра передается условие. Условие должно представлять функцию, которая принимает некоторое значение и возвращает значение типа
bool - true
, если значение соответствует условию, и false
- если не соответствует. В данном случае в качестве такого условия передаем функцию
is_negative, которая вычисляет, является ли число отрицательным. То есть мы удаляем отрицательные числа.
Для вывода вектора на консоль применяется функция print. После выполнения remove_if
на консоль будет выведено
0 1 2 3 4 5 1 2 3 4 5
В результате remove_if()
просто перемещает все элементы, которые нужно сохранить (0 и положительные числа), в начало диапазона. При этом часть из этих чисел остается в конце вектора,
но это не имеет значения, поскольку эта часть вектора будет удалена. А сама функция возвращает итератор iter, который указывает на первый удаляемый элемент.
Далее удаляем все элементы, которые начинаются с этого итератора:
numbers.erase(iter, end(numbers)); print(numbers);
Теперь консольный вывод будет следующим:
0 1 2 3 4 5
Поскольку безопасное удаление из контейнеров представляет довольно часто встречаемую задачу, то начиная со стандарта C++20 в язык С++ были добавлены функции std::erase() и std::erase_if()
Функция std::erase() удаляет отдельное значение из контейнера (не применяется к std::set и std::map):
std::erase(Container, Value)
Функция std::erase_if() удаляет значения из контейнера, которые соответствуют условию:
std::erase_if(Container, Function)
Так, перепишем предыдущий пример с помощью функции std::erase_if
:
#include <iostream> #include <vector> void print (const std::vector<int>& data) { for(const auto& n: data) { std::cout << n << "\t"; } std::cout << std::endl; } bool is_negative(int n){ return n < 0;} int main() { std::vector<int> numbers {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5}; std::erase_if(numbers, is_negative); print(numbers); // 0 1 2 3 4 5 }