4

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

for(auto&& i : container | filtered(predicate)) {
  // .. do stuff
}

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

但 alazy_collect也可能有助于避免复制。

下面的所有答案都很棒。我希望我可以标记所有这些。我不知道std::back_inserter。收集东西现在很容易:

boost::copy( orig | filtered(predicate), std::back_inserter(collection));
4

3 回答 3

6

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

因此,您提出的操作完全超出了标准算法的范围。也许标准中“应该”有大量额外的通用容器操作,包括这个,但“STL 哲学”是在迭代器上操作。

您建议的非惰性操作可以使用std::back_inserterand std::copy_if(可选地使用移动迭代器)或move_if(如果您自己滚动)来完成。std::copy_if从 C++03 中丢失,但这是一个意外的疏忽。

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

我不知道为什么它们没有被合并到 C++11 中,但是关于 C++11 需要注意的一件事是它已经很晚了。由于时间不够,提案被放弃,因此原因可能很简单,“鉴于已知的现有工作量,它从未被认为足够重要以提出提案”。

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

于 2013-03-21T10:15:41.197 回答
5

这个怎么样:

#include <algorithm>
#include <iterator>

std::copy_if(container.begin(), container.end(), std::back_inserter(result), []{...})

container您要从中收集的容器在哪里,以及result将存储该集合的容器。

于 2013-03-21T10:15:18.473 回答
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 << " "; }
}

这是一个活生生的例子

如果你想要一个直接作用于容器而不是由一对迭代器定义的范围的函数,你可以编写以下帮助程序:

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

你会用这种方式:

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

当然还有活生生的例子

于 2013-03-21T10:17:31.710 回答