Подтвердить что ты не робот

Современный способ фильтрации контейнера STL?

Возвращаясь на С++ после нескольких лет С#, мне было интересно, что такое современное - читайте: С++ 11 - способ фильтрации массива будет, то есть как мы можем достичь чего-то подобного этому запросу Linq:

var filteredElements = elements.Where(elm => elm.filterProperty == true);

Чтобы фильтровать вектор элементов (strings для этого вопроса)?

Я искренне надеюсь, что старые алгоритмы стиля STL (или даже расширения, такие как boost::filter_iterator), требующие определения явных методов, теперь заменяются?

4b9b3361

Ответ 1

См. пример cplusplus.com для std::copy_if:

std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar;

// copy only positive numbers:
auto it = std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );

std::copy_if вычисляет lambda выражение для каждого элемента в foo здесь, и если он возвращает true, он копирует значение в bar.

std::back_inserter позволяет нам фактически вставлять новые элементы в конце bar (используя push_back()) с помощью итератора без необходимости сначала изменять его размер до требуемого размера.

Ответ 2

Я думаю, Boost.Range заслуживает упоминания. Полученный код довольно близок к оригиналу:

#include <boost/range/adaptors.hpp>

// ...

using boost::adaptors::filtered;
auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; });

Единственный недостаток - явно указать тип параметра лямбда. Я использовал decltype (elements):: value_type, потому что он избегает необходимости указывать точный тип, а также добавляет зерно общности. Альтернативно, с полиморфными lambdas С++ 14, тип можно просто указать как auto:

auto filteredElements = elements | filtered([](auto const& elm)
    { return elm.filterProperty == true; });

filterElements будет диапазоном, подходящим для обхода, но в основном это представление исходного контейнера. Если вам нужен другой контейнер, заполненный копиями элементов, удовлетворяющих критериям (так что он не зависит от времени жизни исходного контейнера), он может выглядеть так:

using std::back_inserter; using boost::copy; using boost::adaptors::filtered;
decltype(elements) filteredElements;
copy(elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; }), back_inserter(filteredElements));

Ответ 3

Более эффективный подход, если вам действительно не нужен новый экземпляр списка, remove_if, который фактически удаляет элементов из исходного контейнера.

Ответ 4

Мое предложение для С++-эквивалента С#

var filteredElements = elements.Where(elm => elm.filterProperty == true);

Определите функцию шаблона, к которой вы передаете предикат лямбды, чтобы выполнить фильтрацию. Функция шаблона возвращает результат фильтрации. например:

template<typename T>
vector<T> select_T(vector<T> inVec, function<bool(const T&)> predicate)
{
  vector<T> result;
  copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
  return result;
}

для использования - давая тривиальные примеры:

std::vector<int> mVec = {1,4,7,8,9,0};

// filter out values > 5
auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); });

// or > target
int target = 5;
auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });