25 Ranges library [ranges]

25.7 Range adaptors [range.adaptors]

25.7.16 Lazy split view [range.lazy.split]

25.7.16.1 Overview [range.lazy.split.overview]

lazy_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​::​lazy_split denotes a range adaptor object ([range.adaptor.object]).
Given subexpressions E and F, the expression views​::​lazy_split(E, F) is expression-equivalent to lazy_split_view(E, F).
[Example 1: string str{"the quick brown fox"}; for (auto word : str | views::lazy_split(' ')) { for (char ch : word) cout << ch; cout << '*'; } // The above prints the*quick*brown*fox* — end example]

25.7.16.2 Class template lazy_split_view [range.lazy.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 lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> { private: V base_ = V(); // exposition only Pattern pattern_ = Pattern(); // exposition only non-propagating-cache<iterator_t<V>> current_; // exposition only, present only // if forward_range<V> is false // [range.lazy.split.outer], class template lazy_split_view​::​outer-iterator template<bool> struct outer-iterator; // exposition only // [range.lazy.split.inner], class template lazy_split_view​::​inner-iterator template<bool> struct inner-iterator; // exposition only public: lazy_split_view() requires default_initializable<V> && default_initializable<Pattern> = default; constexpr explicit lazy_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 explicit lazy_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> && simple-view<Pattern>> {*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> && simple-view<Pattern>> {*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> lazy_split_view(R&&, P&&) -> lazy_split_view<views::all_t<R>, views::all_t<P>>; template<input_range R> lazy_split_view(R&&, range_value_t<R>) -> lazy_split_view<views::all_t<R>, single_view<range_value_t<R>>>; }
constexpr explicit lazy_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 explicit lazy_split_view(R&& r, range_value_t<R> e);
Effects: Initializes base_ with views​::​all(std​::​forward<R>(r)), and pattern_ with views​::​single(std​::​move(e)).

25.7.16.3 Class template lazy_split_view​::​outer-iterator [range.lazy.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 lazy_split_view<V, Pattern>::outer-iterator { private: using Parent = maybe-const<Const, lazy_split_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only Parent* parent_ = nullptr; // exposition only iterator_t<Base> current_ = iterator_t<Base>(); // exposition only, present only // if V models forward_range bool trailing_empty_ = false; // exposition only public: using iterator_concept = conditional_t<forward_range<Base>, forward_iterator_tag, input_iterator_tag>; using iterator_category = input_iterator_tag; // present only if Base // models forward_range // [range.lazy.split.outer.value], class lazy_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 specifications in [range.lazy.split] 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_, current_ with std​::​move(i.current_), and trailing_empty_ with i.trailing_empty_.
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) { trailing_empty_ = false; return *this; } const auto [pbegin, pend] = subrange{parent_->pattern_}; if (pbegin == pend) ++current; else if constexpr (tiny-range<Pattern>) { current = ranges::find(std::move(current), end, *pbegin); if (current != end) { ++current; if (current == end) trailing_empty_ = true; } } else { do { auto [b, p] = ranges::mismatch(current, end, pbegin, pend); if (p == pend) { current = b; if (current == end) trailing_empty_ = true; 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_ && x.trailing_empty_ == y.trailing_empty_;
friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);
Effects: Equivalent to: return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;

25.7.16.4 Class lazy_split_view​::​outer-iterator​::​value_type [range.lazy.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 lazy_split_view<V, Pattern>::outer-iterator<Const>::value_type : view_interface<value_type> { private: outer-iterator i_ = outer-iterator(); // exposition only constexpr explicit value_type(outer-iterator i); // exposition only public: constexpr inner-iterator<Const> begin() const; constexpr default_sentinel_t end() const noexcept; }; }
constexpr explicit value_type(outer-iterator i);
Effects: Initializes i_ with std​::​move(i).
constexpr inner-iterator<Const> begin() const;
Effects: Equivalent to: return inner-iterator<Const>{i_};
constexpr default_sentinel_t end() const noexcept;
Effects: Equivalent to: return default_sentinel;

25.7.16.5 Class template lazy_split_view​::​inner-iterator [range.lazy.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 lazy_split_view<V, Pattern>::inner-iterator { private: using Base = maybe-const<Const, 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; // present only if Base // models forward_range 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 const iterator_t<Base>& base() const & noexcept; constexpr iterator_t<Base> base() && requires forward_range<V>; constexpr decltype(auto) operator*() const { return *i_.current; } constexpr inner-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 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>>; }; }
If Base does not model forward_range there is no member iterator_category.
Otherwise, 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 const iterator_t<Base>& base() const & noexcept;
Effects: Equivalent to: return i_.current;
constexpr iterator_t<Base> base() && requires forward_range<V>;
Effects: Equivalent to: return std​::​move(i_.current);
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).