实现一种C++20 ranges view: strideview
ranges 是C++20中的重要新增特性。view是ranges的精华,支持组合和惰性求值。ranges还提供了接口,用户可以定制开发自己的view, 如何自行实现view 呢?。view分两种,一种只能作为源,如ranges::ints,而另一种是adaptor,用来调整range或者view的功能。下文是我们自行开发的一种adaptor view: StrideView,和rangs::stride的功能相同:调整步长,如views::ints|stride(2)可以表示偶数。可以看到实现核心的工作是嵌套类adaptor。
#include <vector>
#include <iostream>
#include <range/v3/all.hpp>
namespace views = ranges::views;
template <class Rng>
struct StrideView : ranges::view_adaptor<StrideView<Rng>, Rng> {
constexpr StrideView() = default;
friend ranges::range_access;
using range_type = std::remove_cv_t<Rng>;
struct adaptor : public ranges::adaptor_base {
constexpr adaptor() = default;
template <typename Rng>
auto begin(Rng& rng) noexcept
{
return ranges::begin(rng.base());
}
template <class Rng>
auto end(Rng& rng) noexcept
{
return ranges::end(rng.base());
}
template <typename I>
constexpr auto read(I i) const noexcept
{
return *i;
}
template <class I, class S>
constexpr bool equal(I b, S e) const noexcept
{
return b == e;
}
constexpr adaptor(const StrideView* s) noexcept : strideview_(s) {}
template <typename I>
constexpr auto distance_to(I this_iter, I that_iter) const noexcept requires ranges::sized_range<range_type>
{
auto d = ranges::distance(this_iter, that_iter);
int m = d;
if (d < 0) {
m = -d;
}
int dividor = m / strideview_->stride_;
int carry = (m % (strideview_->stride_) + strideview_->stride_ - 1) / strideview_->stride_;
if (d < 0) {
return -(dividor + carry);
} else {
return dividor + carry;
}
}
template <class I>
requires ranges::bidirectional_range<range_type> constexpr void prev(I& it) noexcept
{
if (it == strideview_->base().end()) {
ranges::advance(it, -strideview_->step_);
} else {
ranges::advance(it, -this->strideview_->stride_);
}
}
template <typename I>
constexpr void next(I& it) noexcept
{
ranges::advance(it, this->strideview_->stride_, strideview_->base().end());
}
template <typename I>
constexpr void advance(I& it, ranges::iter_difference_t<I> n) const noexcept
requires ranges::random_access_range<range_type>
{
if (n >= 0) {
ranges::advance(it, n * this->strideview_->stride_, strideview_->base().end());
} else {
int m = -n;
if (it == strideview_->base().end()) {
ranges::advance(it, -(m - 1) * this->strideview_->stride_ - strideview_->step_);
} else {
ranges::advance(it, n * this->strideview_->stride_);
}
}
}
private:
const StrideView* strideview_;
};
constexpr StrideView(Rng&& rng, int stride) noexcept
requires(ranges::sized_range<Rng>&& ranges::bidirectional_range<Rng>)
: ranges::view_adaptor<StrideView, Rng>(std::forward<Rng>(rng)),
stride_(stride),
size_(rng.size()),
step_(rng.size() % stride)
{
if (!step_) {
step_ = stride;
}
}
constexpr StrideView(Rng&& rng, int stride) noexcept
: ranges::view_adaptor<StrideView, Rng>(std::forward<Rng>(rng)), stride_(stride), step_(stride)
{
}
constexpr auto begin_adaptor() const noexcept { return adaptor{this}; }
constexpr auto end_adaptor() const noexcept { return adaptor{this}; }
private:
int stride_;
int size_;
int step_;
};
template <class Rng>
auto stride(Rng&& rng, int n) noexcept
{
return StrideView<views::all_t<Rng>>(views::all(std::forward<Rng>(rng)), n);
}
auto stride(int s) noexcept
{
return ranges::make_view_closure([s](auto&& rngs) {
using Rngs = decltype(rngs);
return StrideView<views::all_t<Rngs>>(views::all(std::forward<Rngs>(rngs)), s);
});
};
int main()
{
std::vector va = views::ints | views::take(20) | ranges::to<std::vector<int>>;
{
auto s = va | stride(2) | views::drop(2);
for (auto i : s) {
std::cout << i << " ";
}
std::cout << std::endl;
}
return 0;
}
Posted 2020-10-19