如何实现类似std :: copy_if之类的东西,但在插入另一个容器之前应用一个函数

Lam*_*ist 6 c++ stl c++14

完全公开,这可能是在没有需要时尝试使用STL算法的锤子和钉子情况.我在一些C++ 14代码中看到了一个重新出现的模式.我们有一个迭代的容器,如果当前元素匹配某些条件,那么我们将其中一个元素字段复制到另一个容器.

模式类似于:

 for (auto it = std::begin(foo); it!=std::end(foo); ++it){
    auto x = it->Some_member;
    // Note, the check usually uses the field would add to the new container. 
    if(f(x) && g(x)){ 
      bar.emplace_back(x);
    }
  }
Run Code Online (Sandbox Code Playgroud)

这个想法几乎是一个累积,其中所应用的函数并不总是返回一个值.我只能想到一个解决方案

这甚至是个好主意吗?

Yak*_*ont 6

当然.有很多方法.

  1. 找到一个图书馆transform_if,像boost.

  2. 查找一个带有transform_range转换和范围或容器的库,并返回一个带有转换值的范围.用它来撰写copy_if.

  3. 找到一个filter_range类似上面的库.现在,使用std::transform您的过滤范围.

  4. 找到两者,并以适当的顺序组合过滤和转换.现在你的问题只是复制(std::copy或其他).

  5. 编写自己的后插入器包装器,在插入时进行转换.使用它std::copy_if.

  6. 编写自己的范围适配器,如2 3和/或4.

  7. transform_if.


Ice*_*ire 6

您的问题的一个非常通用的解决方案将是以下(工作示例):

#include <iostream>
#include <vector>
using namespace std;

template<typename It, typename MemberType, typename Cond, typename Do>
void process_filtered(It begin, It end, MemberType iterator_traits<It>::value_type::*ptr, Cond condition, Do process)
{
    for(It it = begin; it != end; ++it)
    {
        if(condition((*it).*ptr))
        {
            process((*it).*ptr);
        }
    }
}

struct Data
{
    int x;
    int y;
};

int main()
{
    // thanks to iterator_traits, vector could also be an array;
    // kudos to @Yakk-AdamNevraumont
    vector<Data> lines{{1,2},{4,3},{5,6}};

    // filter even numbers from Data::x and output them
    process_filtered(std::begin(lines), std::end(lines), &Data::x, [](int n){return n % 2 == 0;}, [](int n){cout << n;});

    // output is 4, the only x value that is even

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它不使用STL,这是正确的,但你只是传递一个迭代器对,查找成员和两个lambdas /函数,它们将首先过滤,然后分别使用过滤后的输出.

我喜欢你的一般解决方案,但在这里你不需要有一个提取相应属性的lambda.

显然,代码可以被改进以便使用,const_iterator但是对于一般的想法,我认为它应该是有帮助的.如果您希望将此方法用于封装类,您还可以将其扩展为具有返回成员属性而不是直接成员属性指针的成员函数.

  • `std :: iterator_traits <It> :: value_type`而不是`It :: value_type`(`Foo*`没有`:: value_type`,但确实有`iterator_traits <Foo*> :: value_type`) (2认同)