Идиома 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
}