自定义范围适配器仅在合成结束时有效

Custom range adaptor only works at the end of composition

我正在构建一个自定义范围适配器,我称之为 replicate_view,它应该 return 视图的每个元素 N 次。这是一个例子:

for (auto i : std::views::iota(1, 5) | 
              views::replicate(2))
   std::cout << i << '\n';

这应该打印:1 1 2 2 3 3 4 4

但是,如果我在它后面添加一些东西,它不起作用:

for (auto i : std::views::iota(1, 5) | 
              views::replicate(2) |
              std::views::take(2))
   std::cout << i << '\n';

这给出了错误。

VC++:

error C2678: binary '|': no operator found which takes a left-hand operand of type 'n805::replicate_view<std::ranges::iota_view<_Ty1,_Ty2>>' (or there is no acceptable conversion)

叮当声:

error: invalid operands to binary expression ('replicate_view<std::ranges::views::all_t<iota_view<int, int>>>' (aka 'replicate_view<std::ranges::iota_view<int, int>>') and 'std::__range_adaptor_closure_t<std::__bind_back_t<std::ranges::views::__take::__fn, std::tuple<int>>>')

这是我的实现。

迭代器和哨兵

   template <typename R>
   struct replicate_iterator;

   template <typename R>
   struct replicate_sentinel
   {
      using base = std::ranges::iterator_t<R>;
      using size_type = std::ranges::range_difference_t<R>;

      constexpr replicate_sentinel(base end) : end_{ end } {}
      constexpr bool is_at_end(replicate_iterator<R> it);

   private:
      base      end_;
   };

   template <typename R>
   struct replicate_iterator : std::ranges::iterator_t<R>
   {
      using base = std::ranges::iterator_t<R>;
      using value_type = typename std::ranges::range_value_t<R>;

      constexpr replicate_iterator(base start, std::ranges::range_difference_t<R> count) :
         pos_{ start }, count_{ count }
      {
      }

      constexpr replicate_iterator operator++(int)
      {
         if (step_ == count_)
         {
            step_ = 1;
            pos_++;
         }
         else
         {
            step_++;
         }

         return pos_;
      }

      constexpr replicate_iterator& operator++()
      {
         if (step_ == count_)
         {
            step_ = 1;
            pos_++;
         }
         else
         {
            step_++;
         }

         return (*this);
      }

      constexpr value_type operator*() const
      {
         return *pos_;
      }

      constexpr bool operator==(replicate_sentinel<R> s)
      {
         return s.is_at_end(*this);
      }

      constexpr base const value() const { return pos_; }

   private:
      base                                pos_;
      std::ranges::range_difference_t<R>  count_;
      std::ranges::range_difference_t<R>  step_ = 1;
   };

   template <typename R>
   constexpr bool replicate_sentinel<R>::is_at_end(replicate_iterator<R> it)
   {
      return end_ == it.value();
   }

量程适配器

   template<std::ranges::view R>
   struct replicate_view : public std::ranges::view_interface<replicate_view<R>>
   {
   private:
      R                                   base_;
      std::ranges::range_difference_t<R>  count_;

   public:
      replicate_view() = default;

      constexpr replicate_view(R base, std::ranges::range_difference_t<R> count)
         : base_(std::move(base))
         , count_(count)
      {
      }

      constexpr R base() const&
         requires std::copy_constructible<R>
      { return base_; }
      constexpr R base()&& { return std::move(base_); }

      constexpr std::ranges::range_difference_t<R> const& increment() const { return count_; }

      constexpr auto begin() const
      {
         return replicate_iterator<R>(std::ranges::begin(base_), count_);
      }

      constexpr auto end() const
      {
         return replicate_sentinel<R>{std::ranges::end(base_)};
      }

      constexpr auto size() const requires std::ranges::sized_range<R>
      { return count_ * std::ranges::size(base_); }

      constexpr auto size() requires std::ranges::sized_range<R>
      { return count_ * std::ranges::size(base_); }
   };

推导指南

   template<class R>
   replicate_view(R&& base, std::ranges::range_difference_t<R> count)
      ->replicate_view<std::ranges::views::all_t<R>>;

范围适配器对象

   namespace details
   {
      struct replicate_view_fn_closure
      {
         std::size_t step_;
         constexpr replicate_view_fn_closure(std::size_t step)
            : step_(step)
         {
         }

         template <std::ranges::sized_range R>
         constexpr auto operator()(R&& r) const
         {
            return replicate_view(std::forward<R>(r), step_);
         }
      };

      struct replicate_view_fn
      {
         template<std::ranges::sized_range R>
         constexpr auto operator () (R&& r, std::size_t step) const
         {
            return replicate_view(std::forward<R>(r), step);
         }

         constexpr auto operator () (std::size_t step) const
         {
            return replicate_view_fn_closure(step);
         }
      };

      template <std::ranges::sized_range R>
      constexpr auto operator | (R&& r, replicate_view_fn_closure&& a)
      {
         return std::forward<replicate_view_fn_closure>(a)(std::forward<R>(r));
      }
   }

   namespace views
   {
      inline constexpr details::replicate_view_fn replicate;
   }

范围适配器类型有:

迭代器类型有:

这是一个演示:https://godbolt.org/z/fM5dz5zzf

我不知道缺少了什么。

首先,sentinel_for要求sentinel必须是default_initializable,所以需要为replicate_sentinel添加默认构造函数。

template <typename R>
struct replicate_sentinel
{
  replicate_sentinel() = default;
  // ...
};

其次,sentinel_for还要求const iterator&const sentinel&相媲美,所以replicate_iterator::operator==应该是const合格的(我怀疑这只是打字错误)

template <typename R>
struct replicate_iterator : std::ranges::iterator_t<R>
{
  constexpr bool operator==(replicate_sentinel<R> s) const
  {
      return s.is_at_end(*this);
  }
  // ...
};

Demo


来自巴里:

The way to check this kind of thing for ranges is:

  1. Verify that your iterator is an input_iterator.
  2. Verify that your sentinel is a sentinel_for your iterator.

Those are the checks that will tell you what functionality you're missing.