如何获得向量中满足谓词的唯一指针的子集?
How do I obtain the subset of a unique-ptrs in a vector, which satisfy a predicate?
我有一个包含 std::unique_ptr<Foo>
个对象的向量。我想获得符合某些条件的所有向量项的集合。
我看到了 std 函数,但它们似乎都测试谓词(和 return bool)或 return 单个元素。
是否有内置机制来获取作为矢量子集的集合?如果没有,是否有一种方法可以构建一个迭代器来针对任意谓词测试项目(以识别满足我的条件的项目)和一种机制 return 所有满足该谓词的项目?
注意,因为你有一个 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。用法相同。
如果要将坏 foos 拆分为另一个矢量,可以使用 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());
如果您不再关心 "bad" 个对象,请使用 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());
}
}
由于您的向量包含 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)
);
但是,如果您计划多次进行该迭代,并且不想用时间换取 space,您可能会像@kennytm 的最后一样复制出实际指针,从而获得更好的服务建议的选项。
您要的是 <algorithm>
中的 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_ptr
或weak_ptr
,这样就可以复制了。
我有一个包含 std::unique_ptr<Foo>
个对象的向量。我想获得符合某些条件的所有向量项的集合。
我看到了 std 函数,但它们似乎都测试谓词(和 return bool)或 return 单个元素。
是否有内置机制来获取作为矢量子集的集合?如果没有,是否有一种方法可以构建一个迭代器来针对任意谓词测试项目(以识别满足我的条件的项目)和一种机制 return 所有满足该谓词的项目?
注意,因为你有一个 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。用法相同。
如果要将坏 foos 拆分为另一个矢量,可以使用 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());
如果您不再关心 "bad" 个对象,请使用 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());
}
}
由于您的向量包含 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)
);
但是,如果您计划多次进行该迭代,并且不想用时间换取 space,您可能会像@kennytm 的最后一样复制出实际指针,从而获得更好的服务建议的选项。
您要的是 <algorithm>
中的 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_ptr
或weak_ptr
,这样就可以复制了。