在使用了C#多年之后,我又回到C ++了,我想知道现代的方式-阅读:C ++ 11-筛选数组的方式,即如何实现类似于此Linq查询的内容:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
为了过滤元素向量(strings
出于这个问题)?
我衷心希望现在已取代需要定义显式方法的旧STL样式算法(甚至是扩展boost::filter_iterator
)。
在使用了C#多年之后,我又回到C ++了,我想知道现代的方式-阅读:C ++ 11-筛选数组的方式,即如何实现类似于此Linq查询的内容:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
为了过滤元素向量(strings
出于这个问题)?
我衷心希望现在已取代需要定义显式方法的旧STL样式算法(甚至是扩展boost::filter_iterator
)。
Answers:
请参阅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()
)同一个迭代,而不必首先将它调整到所需的大小。
std::copy_if
仅不过是for循环
如果您实际上不需要列表的新副本,则更有效的方法是remove_if
,它实际上是从原始容器中删除元素。
remove_if
特别喜欢@ATV ,因为它是在出现突变时使用过滤器的方法,比复制整个新列表要快。如果我使用C ++进行过滤,则可以在上使用它copy_if
,因此我认为它会增加。
在C ++ 20中,使用Ranges库中的过滤器视图:(需要#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)。
我认为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));
我对相当于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); });
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; });
filterProperty
设置为的所有元素true
吗?