【问题标题】:How do I obtain the subset of a unique-ptrs in a vector, which satisfy a predicate?如何获得满足谓词的向量中唯一指针的子集?
【发布时间】:2017-03-11 19:56:27
【问题描述】:

我有一个std::unique_ptr<Foo> 对象的向量。我想获取符合某些条件的所有矢量项的集合。 我看到了 std 函数,但它们似乎都在测试谓词(并返回 bool)或返回单个元素。

是否有内置机制来获取作为向量子集的集合?如果没有,有没有办法构造一个迭代器来针对任意谓词测试项目(以识别满足我条件的项目)以及返回所有满足该谓词的项目的机制?

【问题讨论】:

  • std::copy_if 是否满足您的要求? en.cppreference.com/w/cpp/algorithm/copy
  • 你希望返回的子集的类型是什么?原始向量是否要更改?您确实需要更准确地说明您的问题。
  • 我希望子集也是 Foo 指针的向量。我不希望原始向量受到影响。我确实想编辑子集向量中的项目并将这些编辑反映在原始向量中。我假设使用指针应该确保是这种情况。
  • Richard - 我想我将删除 unique_ptr 要求并使用 copy_if。

标签: c++ c++11 iteration unique-ptr predicate


【解决方案1】:

请注意,由于您有一个 unique_ptr 向量,因此这些元素只能移动,即一旦您获得子集,原始向量将不再相同。


破坏性最小的方法是使用std::stable_partition将向量分成两组,同时将所有内容保持在同一个向量中:

auto sep = std::stable_partition(vec.begin(), vec.end(), [](const auto& foo) {
    return foo->is_good();
});
// the part `vec.begin() .. sep` contains all "good" foos.
// the part `sep .. vec.end()` contains all "bad" foos.

如果顺序不重要,use std::partition instead。用法是一样的。

如果你想把坏的 foo 分割成另一个向量,你可以使用std::copy_if + std::make_move_iterator 将对象移出。请注意,这会在各处留下漏洞。使用std::remove 清理它们。

decltype(vec) bad_vec;
std::copy_if(std::make_move_iterator(vec.begin()),
             std::make_move_iterator(vec.end()),
             std::back_inserter(bad_vec),
             [](const auto& p) { return !p->is_good(); });
auto new_end = std::remove(vec.begin(), vec.end(), nullptr);
vec.erase(new_end, vec.end());

如果您不再关心“坏”对象,请使用std::remove_if

auto new_end = std::remove_if(vec.begin(), vec.end(), [](const auto& foo) {
    return !foo->is_good();
});
vec.erase(new_end, vec.end());
// now `vec` only contains "good" foos.

如果您只想获取原始指针,而不是 unique_ptr 本身,您可以使用std::transform 填充vector<Foo*>,然后使用remove_if 过滤它...但此时可能是只是更容易编写 for 循环。

std::vector<int*> good_vec;
for (const auto& foo : vec) {
    if (foo->is_good()) {
        good_vec.push_back(foo.get());
    }
}

【讨论】:

  • std::reference_wrappers 的向量或原始向量的索引是其他非破坏性选项。
【解决方案2】:

由于您的向量包含unique_ptr 的(我们不复制) - 我建议您询问的第二个选项:仅迭代与您的谓词匹配的那些元素的迭代器。这正是boost::filter_iterator

一个例子:

bool points_to_positive(int* ptr) { 
    return ptr != nullptr and *ptr > 0; 
}

// ...

std::vector<std::unique_ptr<int>> vec;

// ...

auto iterator = boost::make_filter_iterator(
    &points_to_positive, std::begin(vec), std::end(vec)
);

但是,如果您计划多次进行该迭代,并且不想以时间换空间,那么您最好只复制实际的指针,就像在@kennytm 的最后一个建议选项中一样。

【讨论】:

  • 如果我删除 unique_ptr 要求,是否有 STL 版本的 filter_iterator?
  • @Craig:如果我没记错的话,标准库有点缺乏与迭代器相关的设施。 ...但自己制作并不是很困难。这是相当多的样板,但除此之外 - 与通常的迭代器相同,除了您的 operator++operator+ 等实现在计算满足谓词的元素时会前进。
【解决方案3】:

您要求的是来自&lt;algorithm&gt;std::copy_if。对于无法复制的unique_ptr 元素,这不是您想要的。示例代码:

#include <algorithm>
#include <array>
#include <cstdlib>
#include <experimental/array>
#include <iostream>
#include <type_traits>
#include <vector>

using std::cout;
using std::endl;
using std::size_t;

bool is_even( const int n )
{
  // True iff n is even.
  return n % 2 == 0;
}

std::ostream& operator<< ( std::ostream& os, const std::vector<int>& container )
{
  // Boilerplate instrumentation.
  for ( const int& x : container )
    os << x << ' ';

  return os;
}

int main(void)
{
  // Our input array, raw:
  constexpr int digits[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  // The number of input elements:
  constexpr size_t ndigits = std::extent<decltype(digits)>();
  // Container wrapping our input array:
  constexpr std::array<int, ndigits > numbers =
    std::experimental::to_array(digits);
  std::vector<int> even_numbers;

  even_numbers.reserve(ndigits); // Upper bound on output size.
  std::copy_if( numbers.cbegin(),
                numbers.cend(),
                std::back_inserter(even_numbers),
                is_even );
  even_numbers.shrink_to_fit();

  // Correct output is "2 4 6 8 "
  cout << even_numbers << endl;

  return EXIT_SUCCESS;
}

但是,您的数组包含无法复制的 unique_ptr 对象。几个答案有其他很好的建议来获得相同的结果。但是,如果您想将满足要求的参考文献复制到不同的集合中,您也可以将unique_ptr 更改为shared_ptrweak_ptr,这样可以复制。

【讨论】:

  • 您确定要建议 OP 复制 unique_ptrs 吗?
  • 在底部添加了一行。其他几个答案提供了很好的选择!这些答案很棒,我认为没有必要重复它们。我刚刚为 OP 提出的问题提供了一些演示代码。
  • 看看unique_ptrs 怎么不能被复制,你的建议会起作用吗? (我试图在这里为您节省投票...)
  • 我在底部添加了一个更强烈的警告,这是 OP 要求的,而不是他或她想要的。
  • 也就是说,将unique_ptr 更改为shared_ptr 或其他可复制类型与使用其他不复制的算法一样有效地修改问题。
猜你喜欢
  • 2021-03-30
  • 2021-11-30
  • 2016-07-26
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
  • 1970-01-01
相关资源
最近更新 更多