24 Ranges library [ranges]

24.7 Range adaptors [range.adaptors]

24.7.11 Split view [range.split]

24.7.11.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.
The name views​::​split denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and F, the expression views​::​split(E, F) is expression-equivalent to split_­view{E, F}.
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
 ]

24.7.11.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
    sized_range<R> &&
    requires { typename require-constant<remove_reference_t<R>::size()>; } &&
    (remove_reference_t<R>::size() <= 1);

  template<input_range V, forward_range Pattern>
    requires view<V> && view<Pattern> &&
             indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
             (forward_range<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 !forward_­range<V>
    // [range.split.outer], class template split_­view​::​outer-iterator
    template<bool> struct outer-iterator;       // exposition only
    // [range.split.inner], class template split_­view​::​inner-iterator
    template<bool> struct inner-iterator;       // exposition only
  public:
    split_view() = default;
    constexpr split_view(V base, Pattern pattern);

    template<input_range R>
      requires constructible_from<V, views::all_t<R>> &&
               constructible_from<Pattern, single_view<range_value_t<R>>>
    constexpr split_view(R&& r, range_value_t<R> e);

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

    constexpr auto begin() {
      if constexpr (forward_range<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 forward_range<V> && forward_range<const V> {
      return outer-iterator<true>{*this, ranges::begin(base_)};
    }

    constexpr auto end() requires forward_range<V> && common_range<V> {
      return outer-iterator<simple-view<V>>{*this, ranges::end(base_)};
    }

    constexpr auto end() const {
      if constexpr (forward_range<V> && forward_range<const V> && common_range<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<views::all_t<R>, views::all_t<P>>;

  template<input_range R>
    split_view(R&&, range_value_t<R>)
      -> split_view<views::all_t<R>, single_view<range_value_t<R>>>;
}
constexpr split_view(V base, Pattern pattern);
Effects: Initializes base_­ with std​::​move(base), and pattern_­ with std​::​move(pattern).
template<input_­range R> requires constructible_­from<V, views::all_t<R>> && constructible_­from<Pattern, single_view<range_value_t<R>>> constexpr split_view(R&& r, range_value_t<R> e);
Effects: Initializes base_­ with views​::​all(std​::​forward<R>(r)), and pattern_­ with single_­view{​std​::​move(e)}.

24.7.11.3 Class template split_­view​::​outer-iterator [range.split.outer]

namespace std::ranges {
  template<input_range V, forward_range Pattern>
    requires view<V> && view<Pattern> &&
             indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
             (forward_range<V> || tiny-range<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 forward_­range
      iterator_t<Base>();

  public:
    using iterator_concept  =
      conditional_t<forward_range<Base>, forward_iterator_tag, input_iterator_tag>;
    using iterator_category = input_iterator_tag;
    // [range.split.outer.value], class split_­view​::​outer-iterator​::​value_­type
    struct value_type;
    using difference_type   = range_difference_t<Base>;

    outer-iterator() = default;
    constexpr explicit outer-iterator(Parent& parent)
      requires (!forward_range<Base>);
    constexpr outer-iterator(Parent& parent, iterator_t<Base> current)
      requires forward_range<Base>;
    constexpr outer-iterator(outer-iterator<!Const> i)
      requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;

    constexpr value_type operator*() const;

    constexpr outer-iterator& operator++();
    constexpr decltype(auto) operator++(int) {
      if constexpr (forward_range<Base>) {
        auto tmp = *this;
        ++*this;
        return tmp;
      } else
        ++*this;
    }

    friend constexpr bool operator==(const outer-iterator& x, const outer-iterator& y)
      requires forward_range<Base>;

    friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);
  };
}
Many of the following specifications refer to the notional member current of outer-iterator.
current is equivalent to current_­ if V models forward_­range, and parent_­->current_­ otherwise.
constexpr explicit outer-iterator(Parent& parent) requires (!forward_­range<Base>);
Effects: Initializes parent_­ with addressof(parent).
constexpr outer-iterator(Parent& parent, iterator_t<Base> current) requires forward_­range<Base>;
Effects: Initializes parent_­ with addressof(parent) and current_­ with std​::​move(current).
constexpr outer-iterator(outer-iterator<!Const> i) requires Const && convertible_­to<iterator_t<V>, iterator_t<Base>>;
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 {
    auto [b, p] = ranges::mismatch(std::move(current), end, pbegin, pend);
    current = std::move(b);
    if (p == pend) {
      break;            // The pattern matched; skip it
    }
  } while (++current != end);
}
return *this;
friend constexpr bool operator==(const outer-iterator& x, const outer-iterator& y) requires forward_­range<Base>;
Effects: Equivalent to: return x.current_­ == y.current_­;
friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);
Effects: Equivalent to: return x.current == ranges​::​end(x.parent_­->base_­);

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

namespace std::ranges {
  template<input_range V, forward_range Pattern>
    requires view<V> && view<Pattern> &&
             indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
             (forward_range<V> || tiny-range<Pattern>)
  template<bool Const>
  struct split_view<V, Pattern>::outer-iterator<Const>::value_type
    : view_interface<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 requires copyable<outer-iterator>;
    constexpr inner-iterator<Const> begin() requires (!copyable<outer-iterator>);
    constexpr default_sentinel_t end() const;
  };
}
constexpr explicit value_type(outer-iterator i);
Effects: Initializes i_­ with std​::​move(i).
constexpr inner-iterator<Const> begin() const requires copyable<outer-iterator>;
Effects: Equivalent to: return inner-iterator<Const>{i_­};
constexpr inner-iterator<Const> begin() requires (!copyable<outer-iterator>);
Effects: Equivalent to: return inner-iterator<Const>{std​::​move(i_­)};
constexpr default_sentinel_t end() const;
Effects: Equivalent to: return default_­sentinel;

24.7.11.5 Class template split_­view​::​inner-iterator [range.split.inner]

namespace std::ranges {
  template<input_range V, forward_range Pattern>
    requires view<V> && view<Pattern> &&
             indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
             (forward_range<V> || tiny-range<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        = range_value_t<Base>;
    using difference_type   = range_difference_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 (forward_range<V>) {
        auto tmp = *this;
        ++*this;
        return tmp;
      } else
        ++*this;
    }

    friend constexpr bool operator==(const inner-iterator& x, const inner-iterator& y)
      requires forward_range<Base>;

    friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t);

    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 indirectly_swappable<iterator_t<Base>>;
  };
}
The typedef-name iterator_­category denotes:
  • forward_­iterator_­tag if iterator_­traits<iterator_­t<Base>>​::​iterator_­category models
    derived_­from<forward_­iterator_­tag>;
  • otherwise, iterator_­traits<iterator_­t<Base>>​::​iterator_­category.
constexpr explicit inner-iterator(outer-iterator<Const> i);
Effects: Initializes i_­ with std​::​move(i).
constexpr inner-iterator& operator++();
Effects: Equivalent to:
incremented_ = true;
if constexpr (!forward_range<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 forward_­range<Base>;
Effects: Equivalent to: return x.i_­.current == y.i_­.current;
friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t);
Effects: Equivalent to:
auto [pcur, pend] = subrange{x.i_.parent_->pattern_};
auto end = ranges::end(x.i_.parent_->base_);
if constexpr (tiny-range<Pattern>) {
  const auto & cur = x.i_.current;
  if (cur == end) return true;
  if (pcur == pend) return x.incremented_;
  return *cur == *pcur;
} else {
  auto cur = x.i_.current;
  if (cur == end) return true;
  if (pcur == pend) return x.incremented_;
  do {
    if (*cur != *pcur) return false;
    if (++pcur == pend) return true;
  } while (++cur != end);
  return false;
}
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 indirectly_­swappable<iterator_t<Base>>;
Effects: Equivalent to ranges​::​iter_­swap(x.i_­.current, y.i_­.current).