24 Ranges library [ranges]

24.7 Range adaptors [range.adaptors]

24.7.12 Split view [range.split]

24.7.12.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 1: 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.12.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.12.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.12.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.12.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).