How you create your own views that interact with existing views with operator |?

Stack Overflow Asked by Omnifarious on December 21, 2020

Why does this code work with the #if 0 block in place, but fails with a fairly complex set of error messages if you remove it? And more importantly, how do I make it the same result as the very similar block above it?

#include <ranges>
#include <iterator>
#include <optional>
#include <string_view>
#include <iostream>
#include <algorithm>

template <::std::ranges::view View,
          typename Pred>
requires ::std::ranges::input_range<View> &&
         ::std::ranges::common_range<View> &&
         ::std::is_object_v<Pred> &&
         ::std::indirect_unary_predicate<const Pred, ::std::ranges::iterator_t<View>>
class skip_after_view : public ::std::ranges::view_interface<skip_after_view<View, Pred>>
   skip_after_view() = default;
   skip_after_view(View v, Pred p)
        : subview_(::std::move(v)), pred_(::std::move(p))
   class iterator;
   friend class iterator;

   auto begin() const {
       return iterator{subview_.begin(), subview_.end(), &pred_};
   auto end() const {
       return iterator{subview_.end(), subview_.end(), &pred_};

   View subview_ = View();
   Pred pred_;

template <typename View, typename Pred>
class skip_after_view<View, Pred>::iterator
   using parent_t = View::iterator;
   using parent_traits = ::std::iterator_traits<parent_t>;
   friend class skip_after_view<View, Pred>;
   using value_type = parent_traits::value_type;
   using reference = parent_traits::reference;
   using pointer = parent_traits::pointer;
   using difference_type = ::std::ptrdiff_t;
   using iterator_category = ::std::input_iterator_tag;

   constexpr iterator() = default;

   auto operator *() { return *me_; }
   auto operator *() const { return *me_; }
   iterator &operator ++() {
       for (bool last_pred = true; last_pred; ) {
          if (end_ != me_) {
              last_pred = (*pred_)(operator *());
          } else {
              last_pred = false;
       return *this;       
   void operator ++(int) {
   bool operator ==(iterator const &a, iterator const &b) {
       return a.me_ == b.me_;

   parent_t me_;
   parent_t end_;
   Pred const *pred_ = nullptr;

   iterator(parent_t const &me, parent_t end, Pred const *pred)
        : me_(me), end_(::std::move(end)), pred_(pred)

template <std::ranges::range Range, typename Pred>
skip_after_view(Range&&) -> skip_after_view<std::ranges::views::all_t<Range>, Pred>;

struct skip_after_adaptor {

   template <typename Pred>
   class closure {
       friend class skip_after_adaptor;
       Pred pred;

       explicit closure(Pred &&p) : pred(::std::move(p)) {}
       template <typename Range>
       auto operator ()(Range &&range) {
          return skip_after_view(::std::forward<Range>(range),
   template <typename Pred>
   auto operator ()(Pred pred) const {
       return closure<Pred>(::std::move(pred));
   template <typename Range, typename Pred>
   auto operator()(Range &&range, Pred &&pred) const {
       return skip_after_view(::std::forward(range), ::std::forward(pred));
   template <typename Range, typename Pred>
   friend auto operator|(Range&& rng, closure<Pred> &&fun) {
       return fun(std::forward<Range>(rng));

constexpr auto skip_after = skip_after_adaptor{};

template <::std::input_iterator it>
void check(it const &)

int main()
    using ::std::string_view;
    using namespace ::std::ranges::views;
    using ::std::ostream_iterator;
    using ::std::ranges::copy;
    using ::std::cout;
    auto after_e = [](char c) { return c == 'e'; };

    constexpr string_view sv{"George Orwell"};
    int sum = 0;
       cout << '[';
       copy(sv | skip_after(after_e) | take(6),
       cout << "]n";
#if 0
       auto tmp = skip_after(after_e) | take(6);
       cout << '[';
       copy(sv | tmp, ostream_iterator<char>(cout));
       cout << "]n";
    return sum;

Obligatory Compiler Explorer link

If what I want isn’t cleanly possible, is there an ugly way to do it? For example, could I make my own composition mechanism and have an ugly bunch of garbage to interface it with the existing views.

One Answer

There's no way to write a range adapter closure object that composes with the standard ones in C++20. The standard library doesn't expose the mechanism it uses for that composition.

sv | skip_after(after_e) | take(6) works because sv | skip_after(after_e) hits your operator|, which produces a view that can then be used with take(6).

There's a good chance that C++23 will expose the composition mechanism.

Answered by T.C. on December 21, 2020

Add your own answers!

Related Questions

std::map::operator[] is more efficient than std::map::insert?

1  Asked on December 27, 2020 by eddieipeace


Splitting a list (?)

1  Asked on December 27, 2020 by superannuated


Python Scrapy how to save data in different files

1  Asked on December 27, 2020 by silver-flash


JS get random value from array and update array

2  Asked on December 27, 2020 by nicolas-schmit


Caught and declared exception in Java?

1  Asked on December 26, 2020 by hrvoje-t


IEnumerable and Recursion using yield return

8  Asked on December 26, 2020 by jamie-dixon


How to parse CSV with node.js?

2  Asked on December 26, 2020 by idarosa


Why this program with for loop give zero when y>5 and x=2

2  Asked on December 26, 2020 by vms


Null pointer exception. How my connection object is pointing to null

2  Asked on December 26, 2020 by monisha-ravi


How do I make contents in HTML by using css

0  Asked on December 26, 2020 by jaeseo-lee


How to show Toaster after logout

2  Asked on December 26, 2020


How to write to a csv within a pandas UDF in pyspark?

0  Asked on December 26, 2020 by codemaster2020


Ask a Question

Get help from others!

© 2022 All rights reserved. Sites we Love: PCI Database, MenuIva, UKBizDB, Menu Kuliner, Sharing RPP