24 Ranges library [ranges]

24.7 Range adaptors [range.adaptors]

24.7.13 Lazy split view [range.lazy.split]

24.7.13.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]

24.7.13.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> // [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 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 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>>{*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> 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 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 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))
.

24.7.13.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_­ 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) { 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_;

24.7.13.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 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 std​::​move(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;

24.7.13.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 &; constexpr iterator_t<Base> base() &&; 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 &;
Effects: Equivalent to: return i_­.current;
constexpr iterator_t<Base> base() &&;
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).