C++收集算法?

gnz*_*lbg 4 c++ algorithm collect c++11

我不时地需要迭代容器元素的一个子集,或者只想提取它们而忽略其余部分.我最终boost::range::adaptors::filtered用来创建这个懒惰的集合.

for(auto&& i : container | filtered(predicate)) {
  // .. do stuff
}
Run Code Online (Sandbox Code Playgroud)

是否有理由在STL中缺少收集算法(如在Ruby的收集中)(我们只有copy_if不相同)?或者反对使用它的任何理由?

可能的实现可能是:

template<class Container, class Predicate>
Container collect(Container&& c, Predicate&& p) {
  Container n;
  for(auto&& i : c) {
    if(p(i)) {
      n.push_back(i);
    }
  }
  return n;
}
Run Code Online (Sandbox Code Playgroud)

但是lazy_collect也可能有助于避免复制.

以下所有答案都很棒.我希望我可以标记所有这些.我不知道std::back_inserter.收集东西现在很简单:

boost::copy( orig | filtered(predicate), std::back_inserter(collection));
Run Code Online (Sandbox Code Playgroud)

Ste*_*sop 6

标准算法不直接在容器上运行(创建或销毁它们,或改变它们的大小).它们在迭代器范围上运行,算法所做的唯一更改是通过迭代器进行赋值.

因此,您提出的操作完全超出了标准算法的范围.也许"应该"在标准中包含一大堆额外的通用容器操作,包括这个,但"STL哲学"是对迭代器进行操作.

您建议的非延迟操作可以使用std::back_inserterstd::copy_if(可选地使用移动迭代器)或move_if(如果您自己滚动)完成.std::copy_if在C++ 03中缺失了,但这是一次意外的疏忽.

懒惰版本不能仅仅通过将标准组件插在一起来完成 - 没有干净的方法将一种算法的"输出"直接链接到另一种算法的"输入"而没有中间存储.这就是为什么Boost提供如此有趣的迭代器以及Ranges的原因.

我不知道他们为什么没有被纳入C++ 11,但有一点需要注意的是C++ 11是因为它已经很晚了.提案因缺乏时间而被删除,因此原因可能很简单,"考虑到已知的现有工作量,它从未被认为是重要的."

关于您的特定实现,并非所有容器都具有push_back,因此您可能不应将其Container用作模板参数名称.PushBackSequence将更具描述性的要求.

  • 另请注意,如果容器具有`insert`而不是`push_back`,则可以使用[`inserter`](http://en.cppreference.com/w/cpp/iterator/inserter)而不是`back_inserter`. (2认同)

Rei*_*ica 5

这个怎么样:

#include <algorithm>
#include <iterator>

std::copy_if(container.begin(), container.end(), std::back_inserter(result), []{...})
Run Code Online (Sandbox Code Playgroud)

container您要从中收集的容器在哪里,并且result是将存储该集合的容器.


And*_*owl 5

来自评论:

使用copy_if时,您需要事先知道需要多少空间

这不是真的.您可以使用std::copy_if一个回插件:

#include <algorithm> // For std::copy_if
#include <iterator> // For std::back_inserter
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> v(10);
    std::iota(begin(v), end(v), 0); // Fills the vector with 0..9

    std::vector<int> out;
    std::copy_if(begin(v), end(v), back_inserter(out), [] (int x) 
    //                             ^^^^^^^^^^^^^^^^^^
    {
        return x > 4; 
    });

    for (auto x : out) { std::cout << x << " "; }
}
Run Code Online (Sandbox Code Playgroud)

这是一个实例.

如果你想要一个直接在容器上工作的函数而不是在一对迭代器定义的范围上,你可以写下面的帮助器:

template<typename C1, typename C2, typename P>
void cont_copy_if(C1&& src, C2&& dst, P&& p)
{
    std::copy_if(
        begin(std::forward<C1>(src)),
        end(std::forward<C1>(src)),
        back_inserter(std::forward<C2>(dst)),
        std::forward<P>(p)
        );
}
Run Code Online (Sandbox Code Playgroud)

你会用这种方式:

int main()
{
    std::vector<int> v(10);
    std::iota(begin(v), end(v), 0);

    std::list<int> out;
//  ^^^^^^^^^
//  You could use a different destination container

    cont_copy_if(v, out, [] (int x) { return x > 4; });
//  ^^^^^^^^^^^^

    for (auto x : out) { std::cout << x << " "; }
}
Run Code Online (Sandbox Code Playgroud)

当然还有现实的例子.