现在,C++20范围实现已经在GCC 10.2中发布,我想知道如何将一个范围视图转换回一个实际的容器,比如vector。我找到了这个问题(Range view to std::vector),它问的是预发布版本的同样问题,但是自从发布以来,是否有任何新的方法可以从视图转换为容器?或者那个问题中的单个答案仍然是最好的解决方案?
最简单的方法是使用range-v3,它恰好为此提供了一种转换运算符。来自示例:
using namespace ranges;
auto vi =
views::for_each(views::ints(1, 10), [](int i) {
return yield_from(views::repeat_n(i, i));
})
| to<std::vector>();
// vi == {1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,...}
否则,链接问题中的答案并不完全准确,因为范围可能没有相同的迭代器和终止器类型,而答案要求它们相同。因此,我们可以做得更好:
template <std::ranges::range R>
auto to_vector(R&& r) {
std::vector<std::ranges::range_value_t<R>> v;
// if we can get a size, reserve that much
if constexpr (requires { std::ranges::size(r); }) {
v.reserve(std::ranges::size(r));
}
// push all the elements
for (auto&& e : r) {
v.push_back(static_cast<decltype(e)&&>(e));
}
return v;
}
以上内容的简化版本,不一定在所有相同的位置上进行预留,通过使用views::common
来解决混合哨兵类型问题:
views::common
解决混合哨兵类型问题。template <std::ranges::range R>
auto to_vector(R&& r) {
auto r_common = r | std::views::common;
return std::vector(r_common.begin(), r_common.end());
}
在这里错过了预留的典型例子将是使用具有O(1) size()
的std::list<T>
调用 to_vector()
进行预留,但一旦进入迭代器,我们就会失去它。
std::ranges::to
,所以你可以简单地使用yourVectorView | std::ranges::to<std::vector>()
;由于自动推导,std::vector
的模板参数有时可以省略。 - o_oTurtle使用Barry的答案并创建一个适配器:
/**
* \brief Creates a to_vector_closure for operator()
*/
struct to_vector_adapter
{
struct closure
{
/**
* \brief Gets a vector of a given range.
* \tparam R type of range that gets converted to a vector.
* \param r range that gets converted to a vector.
* \return vector from the given range.
*/
template<std::ranges::range R>
constexpr auto operator()(R&& r) const
{
auto r_common = r | std::views::common;
std::vector<std::ranges::range_value_t<R>> v;
// if we can get a size, reserve that much
if constexpr (requires { std::ranges::size(r); }) {
v.reserve(std::ranges::size(r));
}
v.insert(v.begin(), r_common.begin(), r_common.end());
return v;
}
};
/**
* \brief Gets a closure to convert the range to a vector.
* \return A to_vector_closure that will convert the range to a vector.
*/
constexpr auto operator()() const -> closure
{
return closure{};
}
template<std::ranges::range R>
constexpr auto operator()(R&& r)
{
return closure{}(r);
}
};
inline to_vector_adapter to_vector;
/**
* \brief A range pipe that results in a vector.
* \tparam R type of range that gets converted to a vector.
* \param r range that gets converted to a vector.
* \param a used to create the vector.
* \return a vector from the given range.
*/
template<std::ranges::range R>
constexpr auto operator|(R&& r, aplasp::planning::to_vector_adapter::closure const& a)
{
return a(std::forward<R>(r));
}
使用这个功能,你可以像下面这样做(其中numbers
是一个int
范围):
std::vector foo
{
numbers
| std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * 2; })
| to_vector();
};
或者
std::vector foo
{
to_vector(
numbers
| std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * 2; }))
};
std::ranges::to<std::vector>(intsView)
或intsView | std::ranges::to<std::vector>()
),以及隐式或显式值类型(std::ranges::to<std::set>(intsView)
或std::ranges::to<std::set<int>>(intView)
)。它还支持分配器。我们在我们的代码库中有一个大致相似的实现,并且经常使用它。有很多情况下,你想要将一堆值收集到一个容器中,std::ranges::to
为此提供了一个很好、简洁、表达力强的API。它甚至支持映射:// Project out the "foo" field of this->m_database
auto map = std::ranges::to<absl::flat_hash_map>(
this->m_database | std::views::transform([&](const auto& row) {
return std::pair{row.first, row.second.foo};
})
);
| std::views::transform(...) | std::ranges::to<...>()
- undefined