【问题标题】:How to implement something like std::copy_if but apply a function before inserting into a different container如何实现类似 std::copy_if 但在插入不同容器之前应用函数
【发布时间】:2019-06-16 03:05:58
【问题描述】:

完全公开,这可能是一个锤子和钉子的情况,在不需要时尝试使用 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);
    }
  }

这个想法几乎是一个累加,所应用的函数并不总是返回一个值。我只能想到一个解决方案,要么

这是个好主意吗?

【问题讨论】:

  • 您对要存储的容器元素做了什么?你真的需要它们还是只是保存它们以获得它们的总和?
  • 通常我们会保存一个 ID 以备后用。
  • 好的。那么你链接到的transform_if 可能就是你想要的。
  • 当 stl 算法是你的锤子,那么几乎每个问题实际上都是钉子;)

标签: c++ stl c++14


【解决方案1】:

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

#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;
}

没错,它不使用 STL,但您只需传递一个迭代器对、要查找的成员和两个 lambdas/函数,它们将分别首先过滤和第二次使用过滤后的输出。

我喜欢您的通用解决方案,但在这里您不需要提取相应属性的 lambda。

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

【讨论】:

  • std::iterator_traits&lt;It&gt;::value_type 而不是It::value_typeFoo* 没有::value_type,但有iterator_traits&lt;Foo*&gt;::value_type
  • @Yakk-AdamNevraumont 很棒的评论,谢谢!我已经改变了这个并且还使用了std::beginstd::end。现在,事实上,它也适用于普通数组
  • 这个函数和它的一个参数都被称为foo——呃……给函数一个有意义的名字怎么样?比如copy_member_if
  • @ArneVogel 我稍微改了一下。不过,这只是概念证明,不是生产就绪代码。不过,感谢您指出。不过,copy_member_if 会产生误导,因为函数更通用
【解决方案2】:

当然。有很多方法。

  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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-30
    • 2020-08-14
    • 1970-01-01
    • 1970-01-01
    • 2014-08-17
    相关资源
    最近更新 更多