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::split(E, F) is expression-equivalent to
split_view{E, F}. [
Example:
string str{"the quick brown fox"};
split_view sentence{str, ' '};
for (auto word : sentence) {
for (char ch : word)
cout << ch;
cout << '*';
}
—
end example ]
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 split_view : public view_interface<split_view<V, Pattern>> {
private:
V base_ = V();
Pattern pattern_ = Pattern();
iterator_t<V> current_ = iterator_t<V>();
template<bool> struct outer-iterator;
template<bool> struct inner-iterator;
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)}.
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 =
conditional_t<Const, const split_view, split_view>;
using Base =
conditional_t<Const, const V, V>;
Parent* parent_ = nullptr;
iterator_t<Base> current_ =
iterator_t<Base>();
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 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;
}
} 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_);
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();
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;
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>;
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 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).