我想知道现代的 - 阅读:C++11 - 过滤数组的方式是什么,即我们如何实现类似于此 C# Linq 查询的东西:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
为了过滤元素向量(
strings
为了这个问题)?
我真诚地希望需要定义
显式方法的旧STL风格算法(甚至像
boost::filter_iterator
这样的扩展)现在已经被取代?
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,也可以使用原始的 range-v3 库,使用
#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 的参数类型。我使用 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));
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# 等价于 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); });