Есть ли лучшая альтернатива std:: remove_if для удаления элементов из вектора?

Задача удаления элементов с определенным свойством из std::vector или другого контейнера поддается реализации функционального стиля: зачем беспокоиться о циклах, освобождении памяти и перемещении данных вокруг?

Однако стандартным способом сделать это в С++ представляется следующая идиома:

std::vector<int> ints;
...
ints.erase(
    std::remove_if(ints.begin(), 
                   ints.end(),
                   [](int x){return x < 0;}),
    ints.end());

В этом примере удаляются все элементы, меньшие нуля, из целочисленного вектора.

Я нахожу его не только уродливым, но и простым в использовании. Понятно, что std::remove_if не может изменить размер вектора (как подсказывает его имя), потому что он пропускает только итераторы. Но многие разработчики, включая меня, не понимают этого в начале.

Итак, есть ли более безопасный и, надеюсь, более элегантный способ достичь этого? Если нет, то почему?

Ответы

Ответ 1

Я нахожу его не только уродливым, но и простым в использовании.

Не волнуйтесь, мы все это сделали с самого начала.

Понятно, что 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;});

Ответ 2

Это станет доступно в компиляторе С++ 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