[range.lazy.split] (original) (raw)

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.

Given subexpressions E and F, the expression views​::​lazy_split(E, F) is expression-equivalent tolazy_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 << '*';} — _end example_]

25.7.16.2 Class template lazy_split_view [range.lazy.split.view]

namespace std::ranges { template<auto> struct require-constant; template<class R> concept tiny-range = 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(); Pattern pattern_ = Pattern(); non-propagating-cache<iterator_t<V>> current_; template<bool> struct outer-iterator; template<bool> struct inner-iterator; 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).

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>; using Base = maybe-const<Const, V>; Parent* parent_ = nullptr; iterator_t<_Base_> current_ = iterator_t<_Base_>(); bool trailing_empty_ = false; public: using iterator_concept = conditional_t<forward_range<_Base_>, forward_iterator_tag, input_iterator_tag>;using iterator_category = input_iterator_tag; 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 Vmodels forward_range, and *_parent__->current_ otherwise.

constexpr explicit _outer-iterator_(_Parent_& parent) requires (![forward_range](range.refinements#concept:forward%5Frange "25.4.5 Other range refinements [range.refinements]")<_Base_>);

Effects: Initializes parent_ with addressof(parent).

constexpr _outer-iterator_(_Parent_& parent, iterator_t<_Base_> current) requires [forward_range](range.refinements#concept:forward%5Frange "25.4.5 Other range refinements [range.refinements]")<_Base_>;

Effects: Initializes parent_ with addressof(parent)and current_ with std​::​move(current).

constexpr _outer-iterator_(_outer-iterator_<!Const> i) requires Const && [convertible_to](concept.convertible#concept:convertible%5Fto "18.4.4 Concept convertible_­to [concept.convertible]")<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; } } while (++current != end);} return *this;

friend constexpr bool operator==(const _outer-iterator_& x, const _outer-iterator_& y) requires [forward_range](range.refinements#concept:forward%5Frange "25.4.5 Other range refinements [range.refinements]")<_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(); constexpr explicit value_type(outer-iterator i); 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>; outer-iterator<Const> i_ = outer-iterator<Const>(); bool incremented_ = false; 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 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_rangethere is no member iterator_category.

Otherwise, the typedef-name iterator_category denotes:

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;

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](range.refinements#concept:forward%5Frange "25.4.5 Other range refinements [range.refinements]")<_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](alg.req.ind.swap#concept:indirectly%5Fswappable "24.3.7.4 Concept indirectly_­swappable [alg.req.ind.swap]")<iterator_t<_Base_>>;

Effects: Equivalent toranges​::​iter_swap(x.i_.current, y.i_.current).