过滤 STL 容器的现代方法?

在使用 C # 多年之后,回到 C + + ,我想知道现代的-read: C + + 11-过滤数组的方法是什么,也就是说,我们如何才能实现类似于 Linq 查询的东西:

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

为了过滤一个向量的元素(为了这个问题的 strings) ?

我衷心希望要求定义 明确的方法的旧的 STL 样式算法(甚至像 boost::filter_iterator这样的扩展)现在已经被取代了?

118479 次浏览

请参见 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()) ,而不必首先将其调整到所需的大小。

我认为 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 的参数类型。我使用 dectype (element) : : value _ type 是因为它避免了必须拼写出确切的类型,而且还增加了一些泛型。或者,使用 C + + 14的多态 lambdas,类型可以简单地指定为 auto:

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

FilteredElement 是一个范围,适合遍历,但它基本上是原始容器的一个视图。如果您需要的是另一个容器,其中包含满足条件的元素的副本(这样它就独立于原始容器的生存期) ,那么它可能看起来像:

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

如果实际上不需要列表的新副本,更有效的方法是 remove_if,它实际上从原始容器中删除了元素。

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

根据 下划线 -d的建议改进 Pjm代码:

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

在 C + + 20中,使用范围库中的过滤器视图: (需要 #include <ranges>)

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

惰性地返回 vec中的偶数元素。

(见 [ range. Adaptor.object ]/4[音域过滤器])


GCC10(现场演示)已经支持这一点。对于 Clang 和更老版本的 GCC,也可以使用原始 range-v3库,使用 #include <range/v3/view/filter.hpp>(或 #include <range/v3/all.hpp>)和 ranges::views名称空间而不是 std::ranges::views(现场演示)。