现代过滤STL容器的方法

Modern way to filter STL container?

本文关键字:方法 STL 过滤      更新时间:2023-10-16

经过多年的C#我想知道什么现代 - 读:C 11-过滤数组的方式,即我们如何实现与此类似的东西LINQ查询:

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

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

我真诚地希望旧的STL样式算法(甚至是boost::filter_iterator这样的扩展),现在需要定义明确方法现在被取代了吗?

请参阅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())的末尾实际插入新元素,而无需首先将其调整到所需的大小。

在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,也可以使用#include <range/v3/view/filter.hpp>(或#include <range/v3/all.hpp>)和ranges::views名称空间而不是std::ranges::views(Live Demo)。

如果您实际上不需要列表的新副本,则是remove_if,它实际上删除了原始容器中的元素。

我认为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的参数类型。我使用了dectType(elements):: value_type,因为它避免了必须拼出确切的类型,并且还增加了一定的通用性。另外,使用C 14的多态性lambdas,可以简单地将类型指定为自动:

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

过滤的元素将是一个适合遍历的范围,但基本上是原始容器的视图。如果您需要的另一个容器填充了满足标准的元素的副本(以便它独立于原始容器的寿命),则看起来像:

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

改进的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; });

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