简体   繁体   English

c++20 范围视图到向量

[英]c++20 ranges view to vector

Now that the C++20 ranges implementation is actually here and released under GCC 10.2, I would like to know how to convert a ranges view back to an actual container, like a vector.现在 C++20 范围实现实际上在这里并在 GCC 10.2 下发布,我想知道如何将范围视图转换回实际容器,如矢量。 I've found this question ( Range view to std::vector ) that asked the same thing for the pre-release version but I would like to know if since its been released, have there been any new methods made to convert from a view to a container?我发现这个问题( Range view to std::vector )对预发布版本提出了同样的问题,但我想知道自发布以来是否有任何新方法从视图转换到一个容器? Or is that single answer on that question still the best solution?还是那个问题的单一答案仍然是最好的解决方案?

Easiest thing to do would be to use range-v3, which has a conversion operator exactly for this.最简单的方法是使用 range-v3,它有一个专门用于此目的的转换运算符。 From the examples :例子

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,...}

Otherwise, the answer in the linked question isn't entirely accurate since a range may not have the same iterator and sentinel types, and the answer requires it.否则,链接问题中的答案并不完全准确,因为范围可能没有相同的迭代器和哨兵类型,而答案需要它。 So we can do a little bit better:所以我们可以做得更好一点:

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

A shorter version of the above, which doesn't necessarily reserve up front in all the same places, addresses the mixed-sentinel-type issue by using 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());
}

The canonical example of missed reserve here would be invoking to_vector() with a std::list<T> - which has an O(1) size() available, which could be used to reserve, but we lose that once we go into the iterators.此处错过保留的典型示例是使用std::list<T>调用to_vector() - 它有一个 O(1) size()可用,可用于保留,但一旦我们将 go 放入迭代器。

Using Barry 's answer and creating an adapter:使用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));
}

With this you can do something like (where numbers is a range of int ):有了这个你可以做类似的事情(其中numbersint的范围):

std::vector foo
{
    numbers
    | std::views::filter([](int n){ return n % 2 == 0; })
    | std::views::transform([](int n){ return n * 2; })
    | to_vector();
};

or或者

std::vector foo
{
    to_vector(
        numbers
        | std::views::filter([](int n){ return n % 2 == 0; })
        | std::views::transform([](int n){ return n * 2; }))
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM