Задача удаления элементов с определенным свойством из std::vector
или другой контейнер подходит для реализации функционального стиля: зачем беспокоиться о циклах, освобождении памяти и правильном перемещении данных?
Однако стандартный способ сделать это в C ++ выглядит следующим образом:
std::vector<int> ints;
...
ints.erase(
std::remove_if(ints.begin(),
ints.end(),
[](int x){return x < 0;}),
ints.end());
В этом примере удаляются все элементы меньше нуля из целочисленного вектора.
Я считаю, что это не только некрасиво, но и легко использовать неправильно. Ясно, что std::remove_if
не может изменить размер вектора (как следует из его названия), потому что он получает только итераторы. Но многие разработчики, в том числе и я, не понимают этого с самого начала.
Так есть ли более безопасный и, надеюсь, более элегантный способ добиться этого? Если нет, то почему?
Я считаю, что это не только некрасиво, но и легко использовать неправильно.
Не волнуйтесь, мы все с самого начала.
Ясно, что std :: remove_if не может изменить размер вектора (как следует из его названия), потому что он только передает итераторы. Но многие разработчики, в том числе и я, не понимают этого с самого начала.
Так же. Это смущает всех. Это, вероятно, не следовало называть remove_if
все эти годы назад Оглядываясь назад, а?
Так есть ли более безопасный и, надеюсь, более элегантный способ добиться этого?
нет
Если нет, то почему?
Потому что это самый безопасный и элегантный способ сохранения производительности при удалении элементов из контейнера, при котором удаление элемента делает недействительными итераторы.
предвидя:
Что я могу сделать?
Да, обернуть эту идиому в функцию
template<class Container, class F>
auto erase_where(Container& c, F&& f)
{
return c.erase(std::remove_if(c.begin(),
c.end(),
std::forward<F>(f)),
c.end());
}
Призыв в мотивирующем примере становится:
auto is_negative = [](int x){return x < 0;};
erase_where(ints, is_negative);
или же
erase_where(ints, [](int x){return x < 0;});
Это станет доступным в C ++ 17-ready компиляторе скоро через std::experimental::erase_if
алгоритм:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <experimental/vector>
int main()
{
std::vector<int> ints { -1, 0, 1 };
std::experimental::erase_if(ints, [](int x){
return x < 0;
});
std::copy(ints.begin(), ints.end(), std::ostream_iterator<int>(std::cout, ","));
}
Живой пример что печатает 0,1