过滤容器的现代方法是什么?

问题描述 投票:0回答:6

我想知道现代的 - 阅读:C++11 - 过滤数组的方式是什么,即我们如何实现类似于此 C# Linq 查询的东西:

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

为了过滤元素向量(

strings
为了这个问题)?

我真诚地希望需要定义

显式方法
的旧STL风格算法(甚至像boost::filter_iterator这样的扩展)现在已经被取代?

c++ c++11 filter std stdvector
6个回答
202
投票

参见 cplusplus.com 的示例

std::copy_if

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

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

std::copy_if
计算
foo
中每个元素的 lambda 表达式,如果返回
true
,则会将值复制到
bar

std::back_inserter
允许我们使用迭代器在
bar
(使用
push_back()
)的末尾实际插入新元素,而无需先将其调整为所需的大小。


128
投票

在 C++20 中,使用范围库中的过滤器视图:(需要

#include <ranges>

// namespace views = std::ranges::views;
vec | views::filter([](int a){ return a % 2 == 0; })

延迟返回

vec
中的偶数元素。

(参见 [range.adaptor.object]/4[range.filter]


GCC 10 已支持此功能(现场演示)。对于 Clang 和旧版本的 GCC,也可以使用原始的 range-v3 库,使用

#include <range/v3/view/filter.hpp>
(或
#include <range/v3/all.hpp>
)和
ranges::views
命名空间而不是
std::ranges::views
live demo)。


79
投票

如果您实际上不需要列表的新副本,则更有效的方法是

remove_if
,它实际上会从原始容器中删除元素。


27
投票

我认为

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; });

唯一的缺点是必须显式声明 lambda 的参数类型。我使用 decltype(elements)::value_type 因为它避免了必须拼写出确切的类型,并且还增加了一定的通用性。或者,使用 C++14 的多态 lambda,类型可以简单地指定为 auto:

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

filteredElements 将是一个范围,适合遍历,但它基本上是原始容器的视图。如果您需要的是另一个容器,其中填充了满足条件的元素的副本(以便它独立于原始容器的生命周期),它可能看起来像:

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));

15
投票

改进了pjm代码,遵循下划线-d建议:

template <typename Cont, typename Pred>
Cont filter(const Cont &container, Pred predicate) {
    Cont result;
    std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate);
    return result;
}

用途:

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

auto filteredVec = filter(myVec, [](int a) { return a > 5; });

15
投票

我对 C# 等价于 C++ 的建议

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

定义一个模板函数,向其中传递 lambda 谓词以进行过滤。模板函数返回过滤后的结果。例如:

template<typename T>
vector<T> select_T(const 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); });
© www.soinside.com 2019 - 2024. All rights reserved.