23 Ranges library [ranges]

23.8 Range adaptors [range.adaptors]

23.8.8 Split view [range.split]

23.8.8.1 Overview [range.split.overview]

split_­view takes a View and a delimiter, and splits the View into subranges on the delimiter.
The delimiter can be a single element or a View of elements.
[Example
:
string str{"the quick brown fox"};
split_view sentence{str, ' '};
for (auto word : sentence) {
  for (char ch : word)
    cout << ch;
  cout << '*';
}
// The above prints: the*quick*brown*fox*
end example
]

23.8.8.2 Class template split_­view [range.split.view]

namespace std::ranges {
  template<auto> struct require-constant;       // exposition only

  template<class R>
  concept tiny-range =                          // exposition only
    SizedRange<R> &&
    requires { typename require-constant<remove_reference_t<R>::size()>; } &&
    (remove_reference_t<R>::size() <= 1);

  template<InputRange V, ForwardRange Pattern>
    requires View<V> && View<Pattern> &&
             IndirectlyComparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to<>> &&
             (ForwardRange<V> || tiny-range<Pattern>)
  class split_view : public view_interface<split_view<V, Pattern>> {
  private:
    V base_ = V();                              // exposition only
    Pattern pattern_ = Pattern();               // exposition only
    iterator_t<V> current_ = iterator_t<V>();   // exposition only, present only if !ForwardRange<V>
    template<bool> struct outer_iterator;       // exposition only
    template<bool> struct inner_iterator;       // exposition only
  public:
    split_view() = default;
    constexpr split_view(V base, Pattern pattern);

    template<InputRange R, ForwardRange P>
      requires Constructible<V, all_view<R>> &&
               Constructible<Pattern, all_view<P>>
    constexpr split_view(R&& r, P&& p);

    template<InputRange R>
      requires Constructible<V, all_view<R>> &&
               Constructible<Pattern, single_view<iter_value_t<iterator_t<R>>>>
    constexpr split_view(R&& r, iter_value_t<iterator_t<R>> e);

    constexpr auto begin() {
      if constexpr (ForwardRange<V>)
        return outer_iterator<simple-view<V>>{*this, ranges::begin(base_)};
      else {
        current_ = ranges::begin(base_);
        return outer_iterator<false>{*this};
      }
    }

    constexpr auto begin() const requires ForwardRange<V> && ForwardRange<const V> {
      return outer_iterator<true>{*this, ranges::begin(base_)};
    }

    constexpr auto end() requires ForwardRange<V> && CommonRange<V> {
      return outer_iterator<simple-view<V>>{*this, ranges::end(base_)};
    }

    constexpr auto end() const {
      if constexpr (ForwardRange<V> && ForwardRange<const V> && CommonRange<const V>)
        return outer_iterator<true>{*this, ranges::end(base_)};
      else
        return default_sentinel;
    }
  };

  template<class R, class P>
    split_view(R&&, P&&) -> split_view<all_view<R>, all_view<P>>;

  template<InputRange R>
    split_view(R&&, iter_value_t<iterator_t<R>>)
      -> split_view<all_view<R>, single_view<iter_value_t<iterator_t<R>>>>;
}
constexpr split_view(V base, Pattern pattern);
Effects: Initializes base_­ with std::move(base), and pattern_­ with std::move(pattern).
template<InputRange R, ForwardRange P> requires Constructible<V, all_view<R>> && Constructible<Pattern, all_view<P>> constexpr split_view(R&& r, P&& p);
Effects: Initializes base_­ with view::all(std::forward<R>(r)) and pattern_­ with view::all(std​::forward<P>(p)).
template<InputRange R> requires Constructible<V, all_view<R>> && Constructible<Pattern, single_view<iter_value_t<iterator_t<R>>>> constexpr split_view(R&& r, iter_value_t<iterator_t<R>> e);
Effects: Initializes base_­ with view::all(std::forward<R>(r)) and pattern_­ with single_­view{​std::move(e)}.

23.8.8.3 Class template split_­view​::​outer_­iterator [range.split.outer]

namespace std::ranges {
  template<class V, class Pattern>
  template<bool Const>
  struct split_view<V, Pattern>::outer_iterator {
  private:
    using Parent =                              // exposition only
      conditional_t<Const, const split_view, split_view>;
    using Base   =                              // exposition only
      conditional_t<Const, const V, V>;
    Parent* parent_ = nullptr;                  // exposition only
    iterator_t<Base> current_ =                 // exposition only, present only if V models ForwardRange
      iterator_t<Base>();

  public:
    using iterator_concept  =
      conditional_t<ForwardRange<Base>, forward_iterator_tag, input_iterator_tag>;
    using iterator_category = input_iterator_tag;
    struct value_type;                                  // see [range.split.outer.value]
    using difference_type   = iter_difference_t<iterator_t<Base>>;

    outer_iterator() = default;
    constexpr explicit outer_iterator(Parent& parent)
      requires (!ForwardRange<Base>);
    constexpr outer_iterator(Parent& parent, iterator_t<Base> current)
      requires ForwardRange<Base>;
    constexpr outer_iterator(outer_iterator<!Const> i)
      requires Const && ConvertibleTo<iterator_t<V>, iterator_t<const V>>;

    constexpr value_type operator*() const;

    constexpr outer_iterator& operator++();
    constexpr decltype(auto) operator++(int) {
      if constexpr (ForwardRange<Base>) {
        auto tmp = *this;
        ++*this;
        return tmp;
      } else
        ++*this;
    }

    friend constexpr bool operator==(const outer_iterator& x, const outer_iterator& y)
      requires ForwardRange<Base>;
    friend constexpr bool operator!=(const outer_iterator& x, const outer_iterator& y)
      requires ForwardRange<Base>;

    friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t);
    friend constexpr bool operator==(default_sentinel_t, const outer_iterator& x);
    friend constexpr bool operator!=(const outer_iterator& x, default_sentinel_t y);
    friend constexpr bool operator!=(default_sentinel_t y, const outer_iterator& x);
  };
}
Many of the following specifications refer to the notional member current of outer_­iterator.
current is equivalent to current_­ if V models ForwardRange, and parent_­->current_­ otherwise.
constexpr explicit outer_iterator(Parent& parent) requires (!ForwardRange<Base>);
Effects: Initializes parent_­ with addressof(parent).
constexpr outer_iterator(Parent& parent, iterator_t<Base> current) requires ForwardRange<Base>;
Effects: Initializes parent_­ with addressof(parent) and current_­ with current.
constexpr outer_iterator(outer_iterator<!Const> i) requires Const && ConvertibleTo<iterator_t<V>, iterator_t<const V>>;
Effects: Initializes parent_­ with i.parent_­ and current_­ with std::move(i.current_­).
constexpr value_type operator*() const;
Effects: Equivalent to: return value_­type{*this};
constexpr outer_iterator& operator++();
Effects: Equivalent to:
const auto end = ranges::end(parent_->base_);
if (current == end) return *this;
const auto [pbegin, pend] = subrange{parent_->pattern_};
if (pbegin == pend) ++current;
else {
  do {
    const auto [b, p] = ranges::mismatch(current, end, pbegin, pend);
    if (p == pend) {
      current = b;  // The pattern matched; skip it
      break;
    }
  } while (++current != end);
}
return *this;
friend constexpr bool operator==(const outer_iterator& x, const outer_iterator& y) requires ForwardRange<Base>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator!=(const outer_iterator& x, const outer_iterator& y) requires ForwardRange<Base>;
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator==(const outer_iterator& x, default_sentinel_t); friend constexpr bool operator==(default_sentinel_t, const outer_iterator& x);
Effects: Equivalent to: return x.current == ranges::end(x.parent_­->base_­);
friend constexpr bool operator!=(const outer_iterator& x, default_sentinel_t y); friend constexpr bool operator!=(default_sentinel_t y, const outer_iterator& x);
Effects: Equivalent to: return !(x == y);

23.8.8.4 Class split_­view​::​outer_­iterator​::​value_­type [range.split.outer.value]

namespace std::ranges {
  template<class V, class Pattern>
  template<bool Const>
  struct split_view<V, Pattern>::outer_iterator<Const>::value_type {
  private:
    outer_iterator i_ = outer_iterator();               // exposition only
  public:
    value_type() = default;
    constexpr explicit value_type(outer_iterator i);

    constexpr inner_iterator<Const> begin() const;
    constexpr default_sentinel_t end() const;
  };
}
constexpr explicit value_type(outer_iterator i);
Effects: Initializes i_­ with i.
constexpr inner_iterator<Const> begin() const;
Effects: Equivalent to: return inner_­iterator<Const>{i_­};
constexpr default_sentinel_t end() const;
Effects: Equivalent to: return default_­sentinel;

23.8.8.5 Class template split_­view​::​inner_­iterator [range.split.inner]

namespace std::ranges {
  template<class V, class Pattern>
  template<bool Const>
  struct split_view<V, Pattern>::inner_iterator {
  private:
    using Base =
      conditional_t<Const, const V, V>;                 // exposition only
    outer_iterator<Const> i_ = outer_iterator<Const>(); // exposition only
    bool incremented_ = false;                          // exposition only
  public:
    using iterator_concept  = typename outer_iterator<Const>::iterator_concept;
    using iterator_category = see below;
    using value_type        = iter_value_t<iterator_t<Base>>;
    using difference_type   = iter_difference_t<iterator_t<Base>>;

    inner_iterator() = default;
    constexpr explicit inner_iterator(outer_iterator<Const> i);

    constexpr decltype(auto) operator*() const { return *i_.current; }

    constexpr inner_iterator& operator++();
    constexpr decltype(auto) operator++(int) {
      if constexpr (ForwardRange<V>) {
        auto tmp = *this;
        ++*this;
        return tmp;
      } else
        ++*this;
    }

    friend constexpr bool operator==(const inner_iterator& x, const inner_iterator& y)
      requires ForwardRange<Base>;
    friend constexpr bool operator!=(const inner_iterator& x, const inner_iterator& y)
      requires ForwardRange<Base>;

    friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t);
    friend constexpr bool operator==(default_sentinel_t, const inner_iterator& x);
    friend constexpr bool operator!=(const inner_iterator& x, default_sentinel_t y);
    friend constexpr bool operator!=(default_sentinel_t y, const inner_iterator& x);

    friend constexpr decltype(auto) iter_move(const inner_iterator& i)
    noexcept(noexcept(ranges::iter_move(i.i_.current))) {
      return ranges::iter_move(i.i_.current);
    }

    friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y)
      noexcept(noexcept(ranges::iter_swap(x.i_.current, y.i_.current)))
      requires IndirectlySwappable<iterator_t<Base>>;
  };
}
The typedef-name iterator_­category denotes forward_­iterator_­tag if iterator_­traits<iterator_­t<Base>>::iterator_­category models DerivedFrom<forward_­iterator_­tag>, and input_­iterator_­tag otherwise.
constexpr explicit inner_iterator(outer_iterator<Const> i);
Effects: Initializes i_­ with i.
constexpr inner_iterator& operator++() const;
Effects: Equivalent to:
incremented_ = true;
if constexpr (!ForwardRange<Base>) {
  if constexpr (Pattern::size() == 0) {
    return *this;
  }
}
++i_.current;
return *this;
friend constexpr bool operator==(const inner_iterator& x, const inner_iterator& y) requires ForwardRange<Base>;
Effects: Equivalent to: return x.i_­.current_­ == y.i_­.current_­;
friend constexpr bool operator!=(const inner_iterator& x, const inner_iterator& y) requires ForwardRange<Base>;
Effects: Equivalent to: return !(x == y);
friend constexpr bool operator==(const inner_iterator& x, default_sentinel_t); friend constexpr bool operator==(default_sentinel_t, const inner_iterator& x);
Effects: Equivalent to:
auto cur = x.i_.current;
auto end = ranges::end(x.i_.parent_->base_);
if (cur == end) return true;
auto [pcur, pend] = subrange{x.i_.parent_->pattern_};
if (pcur == pend) return x.incremented_;
do {
  if (*cur != *pcur) return false;
  if (++pcur == pend) return true;
} while (++cur != end);
return false;
friend constexpr bool operator!=(const inner_iterator& x, default_sentinel_t y); friend constexpr bool operator!=(default_sentinel_t y, const inner_iterator& x);
Effects: Equivalent to: return !(x == y);
friend constexpr void iter_swap(const inner_iterator& x, const inner_iterator& y) noexcept(noexcept(ranges::iter_swap(x.i_.current, y.i_.current))) requires IndirectlySwappable<iterator_t<Base>>;
Effects: Equivalent to ranges::iter_­swap(x.i_­.current, y.i_­.current).

23.8.8.6 view​::​split [range.split.adaptor]

The name view::split denotes a range adaptor object.
For some subexpressions E and F, the expression view::split(E, F) is expression-equivalent to split_­view{E, F}.