[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 (<_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 (<_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:
- forward_iterator_tag ifiterator_traits<iterator_t<_Base_>>::iterator_category modelsderived_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;
Effects: Equivalent to: return std::move(i_.current);
constexpr _inner-iterator_& operator++();
Effects: Equivalent to:incremented_ = true;if constexpr (<_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).