C++ 过滤STL容器的现代方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21204676/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Modern way to filter STL container?
提问by ATV
Coming back to C++ after years of C# I was wondering what the modern - read: C++11 - way of filtering an array would be, i.e. how can we achieve something similar to this Linq query:
在多年的 C# 之后回到 C++ 我想知道现代 - 阅读:C++ 11 - 过滤数组的方式是什么,即我们如何实现类似于这个 Linq 查询的东西:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
In order to filter a vector of elements (strings
for the sake of this question)?
为了过滤元素向量(strings
为了这个问题)?
I sincerely hope the old STL style algorithms (or even extensions like boost::filter_iterator
) requiring explicit methodsto be defined are superseded by now?
我真诚地希望现在能取代需要定义显式方法的旧 STL 风格算法(甚至像 的扩展boost::filter_iterator
)?
回答by Sebastian Hoffmann
See the example from cplusplus.com for std::copy_if
:
请参阅 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
evaluates the lambda expression for every element in foo
here and if it returns true
it copies the value to bar
.
std::copy_if
计算foo
此处每个元素的 lambda 表达式,如果返回true
,则将值复制到bar
.
The std::back_inserter
allows us to actually insert new elements at the end of bar
(using push_back()
) with an iterator without having to resize it to the required size first.
这std::back_inserter
允许我们bar
使用push_back()
迭代器在(使用)的末尾实际插入新元素,而无需先将其调整为所需的大小。
回答by djhaskin987
回答by user2478832
I think Boost.Rangedeserves a mention too. The resulting code is pretty close to the original:
我认为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; });
The only downside is having to explicitly declare the lambda's parameter type. I used decltype(elements)::value_type because it avoids having to spell out the exact type, and also adds a grain of genericity. Alternatively, with C++14's polymorphic lambdas, the type could be simply specified as auto:
唯一的缺点是必须显式声明 lambda 的参数类型。我使用了 decltype(elements)::value_type 因为它避免了必须拼出确切的类型,并且还增加了一些通用性。或者,使用 C++14 的多态 lambdas,可以简单地将类型指定为 auto:
auto filteredElements = elements | filtered([](auto const& elm)
{ return elm.filterProperty == true; });
filteredElements would be a range, suitable for traversal, but it's basically a view of the original container. If what you need is another container filled with copies of the elements satisfying the criteria (so that it's independent from the lifetime of the original container), it could look like:
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));
回答by L. F.
In C++20, use filter view from the ranges library: (requires #include <ranges>
)
在 C++20 中,使用范围库中的过滤器视图:(需要#include <ranges>
)
// namespace views = std::ranges::views;
vec | views::filter([](int a){ return a % 2 == 0; })
lazily returns the even elements in vec
.
懒惰地返回 中的偶数元素vec
。
(See [range.adaptor.object]/4and [range.filter])
(见[range.adaptor.object]/4和[range.filter])
This is already supported by GCC 10 (live demo). For Clang and older versions of GCC, the original range-v3 library can be used too, with #include <range/v3/view/filter.hpp>
(or #include <range/v3/all.hpp>
) and the ranges::views
namespace instead of std::ranges::views
(live demo).
GCC 10(现场演示)已经支持这一点。对于 Clang 和旧版本的 GCC,也可以使用原始 range-v3 库,使用#include <range/v3/view/filter.hpp>
(or #include <range/v3/all.hpp>
) 和ranges::views
命名空间而不是std::ranges::views
( live demo)。
回答by pjm
My suggestion for C++ equivalent of C#
我对 C++ 等效于 C# 的建议
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Define a template function to which you pass a lambda predicate to do the filtering. The template function returns the filtered result. eg:
定义一个模板函数,您向该函数传递 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;
}
to use - giving a trivial examples:
使用 - 举一个简单的例子:
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); });
回答by Alex P.
Improved pjmcode following underscore-dsuggestions:
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;
}
Usage:
用法:
std::vector<int> myVec = {1,4,7,8,9,0};
auto filteredVec = filter(myVec, [](int a) { return a > 5; });