Skip to content

Commit 04fb7cb

Browse files
jensmaurertkoeppe
authored andcommitted
P2443R1 views::chunk_by
1 parent 2aeff90 commit 04fb7cb

File tree

2 files changed

+337
-0
lines changed

2 files changed

+337
-0
lines changed

source/ranges.tex

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,13 @@
428428
enable_borrowed_range<V>;
429429

430430
namespace views { inline constexpr @\unspec@ slide = @\unspec@; }
431+
432+
// \ref{range.chunk.by}, chunk by view
433+
template<@\libconcept{forward_range}@ V, @\libconcept{indirect_binary_predicate}@<iterator_t<V>, iterator_t<V>> Pred>
434+
requires @\libconcept{view}@<V> && is_object_v<Pred>
435+
class chunk_by_view;
436+
437+
namespace views { inline constexpr @\unspec@ chunk_by = @\unspec@; }
431438
}
432439

433440
namespace std {
@@ -12532,3 +12539,332 @@
1253212539
\returns
1253312540
\tcode{y.\exposid{end_} - x.\exposid{last_ele_}}.
1253412541
\end{itemdescr}
12542+
12543+
\rSec2[range.chunk.by]{Chunk by view}
12544+
12545+
\rSec3[range.chunk.by.overview]{Overview}
12546+
12547+
\pnum
12548+
\tcode{chunk_by_view} takes a \libconcept{view} and a predicate, and
12549+
splits the \libconcept{view} into \tcode{subrange}s
12550+
between each pair of adjacent elements
12551+
for which the predicate returns \tcode{false}.
12552+
12553+
\pnum
12554+
\indexlibrarymember{chunk_by}{views}%
12555+
The name \tcode{views::chunk_by} denotes
12556+
a range adaptor object\iref{range.adaptor.object}.
12557+
Given subexpressions \tcode{E} and \tcode{F},
12558+
the expression \tcode{views::chunk_by(E, F)} is expression-equivalent to
12559+
\tcode{chunk_by_view(E, F)}.
12560+
\begin{example}
12561+
\begin{codeblock}
12562+
vector v = {1, 2, 2, 3, 0, 4, 5, 2};
12563+
12564+
for (auto r : v | views::chunk_by(ranges::less_equal{})) {
12565+
cout << '[';
12566+
auto sep = "";
12567+
for(auto i : r) {
12568+
cout << sep << i;
12569+
sep = ", ";
12570+
}
12571+
cout << "] ";
12572+
}
12573+
\end{codeblock}
12574+
The above prints: \tcode{[1, 2, 2, 3] [0, 4, 5] [2]}
12575+
\end{example}
12576+
12577+
\rSec3[range.chunk.by.view]{Class template \tcode{chunk_by_view}}
12578+
12579+
\begin{codeblock}
12580+
namespace std::ranges {
12581+
template<@\libconcept{forward_range}@ V, @\libconcept{indirect_binary_predicate}@<iterator_t<V>, iterator_t<V>> Pred>
12582+
requires @\libconcept{view}@<V> && is_object_v<Pred>
12583+
class chunk_by_view : public view_interface<chunk_by_view<V, Pred>> {
12584+
V @\exposid{base_}@ = V(); // \expos
12585+
@\exposid{copyable-box}@<Pred> @\exposid{pred_}@ = Pred(); // \expos
12586+
12587+
// \ref{range.chunk.by.iter}, class \tcode{chunk_by_view::\exposid{iterator}}
12588+
class @\exposid{iterator}@; // \expos
12589+
12590+
public:
12591+
chunk_by_view() requires @\libconcept{default_initializable}@<V> && @\libconcept{default_initializable}@<Pred> = default;
12592+
constexpr explicit chunk_by_view(V base, Pred pred);
12593+
12594+
constexpr V base() const & requires @\libconcept{copy_constructible}@<V> { return @\exposid{base_}@; }
12595+
constexpr V base() && { return std::move(@\exposid{base_}@); }
12596+
12597+
constexpr const Pred& pred() const;
12598+
12599+
constexpr @\exposid{iterator}@ begin();
12600+
constexpr auto end();
12601+
12602+
constexpr iterator_t<V> @\exposid{find-next}@(iterator_t<V>); // \expos
12603+
constexpr iterator_t<V> @\exposid{find-prev}@(iterator_t<V>) // \expos
12604+
requires @\libconcept{bidirectional_range}@<V>;
12605+
};
12606+
12607+
template<class R, class Pred>
12608+
chunk_by_view(R&&, Pred) -> chunk_by_view<views::all_t<R>, Pred>;
12609+
}
12610+
\end{codeblock}
12611+
12612+
\begin{itemdecl}
12613+
constexpr explicit chunk_by_view(V base, Pred pred);
12614+
\end{itemdecl}
12615+
12616+
\begin{itemdescr}
12617+
\pnum
12618+
\effects
12619+
Initializes \exposid{base_} with \tcode{std::move(base)} and
12620+
\exposid{pred_} with \tcode{std::move(pred)}.
12621+
\end{itemdescr}
12622+
12623+
\indexlibrarymember{pred}{chunk_by_view}%
12624+
\begin{itemdecl}
12625+
constexpr const Pred& pred() const;
12626+
\end{itemdecl}
12627+
12628+
\begin{itemdescr}
12629+
\pnum
12630+
\effects
12631+
Equivalent to: \tcode{return *\exposid{pred_};}
12632+
\end{itemdescr}
12633+
12634+
\begin{itemdecl}
12635+
constexpr @\exposid{iterator}@ begin();
12636+
\end{itemdecl}
12637+
12638+
\begin{itemdescr}
12639+
\pnum
12640+
\expects
12641+
\tcode{\exposid{pred_}.has_value()} is \tcode{true}.
12642+
12643+
\pnum
12644+
\returns
12645+
\tcode{\exposid{iterator}(*this, ranges::begin(\exposid{base_}), \exposid{find-next}(ranges::begin(\exposid{base_})))}.
12646+
12647+
\pnum
12648+
\remarks
12649+
In order to provide
12650+
the amortized constant-time complexity required by the \libconcept{range} concept,
12651+
this function caches the result within the \tcode{chunk_by_view}
12652+
for use on subsequent calls.
12653+
\end{itemdescr}
12654+
12655+
\begin{itemdecl}
12656+
constexpr auto end();
12657+
\end{itemdecl}
12658+
12659+
\begin{itemdescr}
12660+
\pnum
12661+
\effects
12662+
Equivalent to:
12663+
\begin{codeblock}
12664+
if constexpr (@\libconcept{common_range}@<V>) {
12665+
return @\exposid{iterator}@(*this, ranges::end(@\exposid{base_}@), ranges::end(@\exposid{base_}@));
12666+
} else {
12667+
return default_sentinel;
12668+
}
12669+
\end{codeblock}
12670+
\end{itemdescr}
12671+
12672+
\begin{itemdecl}
12673+
constexpr iterator_t<V> @\exposid{find-next}@(iterator_t<V> current);
12674+
\end{itemdecl}
12675+
12676+
\begin{itemdescr}
12677+
\pnum
12678+
\expects
12679+
\tcode{\exposid{pred_}.has_value()} is \tcode{true}.
12680+
12681+
\pnum
12682+
\returns
12683+
\begin{codeblock}
12684+
ranges::next(ranges::adjacent_find(current, ranges::end(@\exposid{base_}@), not_fn(ref(*@\exposid{pred_}@))),
12685+
1, ranges::end(@\exposid{base_}@))
12686+
\end{codeblock}
12687+
\end{itemdescr}
12688+
12689+
\begin{itemdecl}
12690+
constexpr iterator_t<V> @\exposid{find-prev}@(iterator_t<V> current) requires @\libconcept{bidirectional_range}@<V>;
12691+
\end{itemdecl}
12692+
12693+
\begin{itemdescr}
12694+
\pnum
12695+
\expects
12696+
\begin{itemize}
12697+
\item
12698+
\tcode{current} is not equal to \tcode{ranges::begin(\exposid{base_})}.
12699+
\item
12700+
\tcode{\exposid{pred_}.has_value()} is \tcode{true}.
12701+
\end{itemize}
12702+
12703+
\pnum
12704+
\returns
12705+
An iterator \tcode{i}
12706+
in the range \range{ranges::begin(\exposid{base_})}{current} such that:
12707+
\begin{itemize}
12708+
\item
12709+
\tcode{ranges::adjacent_find(i, current, not_fn(ref(*\exposid{pred_})))} is equal to \tcode{current}; and
12710+
\item
12711+
if \tcode{i} is not equal to \tcode{ranges::begin(\exposid{base_})},
12712+
then \tcode{bool(invoke(*\exposid{pred_}, *ranges::prev(i), *i))}
12713+
is \tcode{false}.
12714+
\end{itemize}
12715+
\end{itemdescr}
12716+
12717+
\rSec3[range.chunk.by.iter]{Class \tcode{chunk_by_view::\exposid{iterator}}}
12718+
12719+
\begin{codeblock}
12720+
namespace std::ranges {
12721+
template<@\libconcept{forward_range}@ V, @\libconcept{indirect_binary_predicate}@<iterator_t<V>, iterator_t<V>> Pred>
12722+
requires @\libconcept{view}@<V> && is_object_v<Pred>
12723+
class chunk_by_view<V, Pred>::@\exposid{iterator}@ {
12724+
chunk_by_view* @\exposid{parent_}@ = nullptr; // \expos
12725+
iterator_t<V> @\exposid{current_}@ = iterator_t<V>(); // \expos
12726+
iterator_t<V> @\exposid{next_}@ = iterator_t<V>(); // \expos
12727+
12728+
constexpr @\exposid{iterator}@(chunk_by_view& parent, iterator_t<V> current, // \expos
12729+
iterator_t<V> next);
12730+
12731+
public:
12732+
using value_type = subrange<iterator_t<V>>;
12733+
using difference_type = range_difference_t<V>;
12734+
using iterator_category = input_iterator_tag;
12735+
using iterator_concept = @\seebelow@;
12736+
12737+
@\exposid{iterator}@() = default;
12738+
12739+
constexpr value_type operator*() const;
12740+
constexpr @\exposid{iterator}@& operator++();
12741+
constexpr @\exposid{iterator}@ operator++(int);
12742+
12743+
constexpr @\exposid{iterator}@& operator--() requires @\libconcept{bidirectional_range}@<V>;
12744+
constexpr @\exposid{iterator}@ operator--(int) requires @\libconcept{bidirectional_range}@<V>;
12745+
12746+
friend constexpr bool operator==(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y);
12747+
friend constexpr bool operator==(const @\exposid{iterator}@& x, default_sentinel_t);
12748+
};
12749+
}
12750+
\end{codeblock}
12751+
12752+
\pnum
12753+
\tcode{\exposid{iterator}::iterator_concept} is defined as follows:
12754+
\begin{itemize}
12755+
\item
12756+
If \tcode{V} models \libconcept{bidirectional_range},
12757+
then \tcode{iterator_concept} denotes \tcode{bidirectional_iterator_tag}.
12758+
\item
12759+
Otherwise, \tcode{iterator_concept} denotes \tcode{forward_iterator_tag}.
12760+
\end{itemize}
12761+
12762+
\begin{itemdecl}
12763+
constexpr @\exposid{iterator}@(chunk_by_view& parent, iterator_t<V> current, iterator_t<V> next);
12764+
\end{itemdecl}
12765+
12766+
\begin{itemdescr}
12767+
\pnum
12768+
\effects
12769+
Initializes \exposid{parent_} with \tcode{addressof(parent)},
12770+
\exposid{current_} with \tcode{current}, and
12771+
\exposid{next_} with \tcode{next}.
12772+
\end{itemdescr}
12773+
12774+
\begin{itemdecl}
12775+
constexpr value_type operator*() const;
12776+
\end{itemdecl}
12777+
12778+
\begin{itemdescr}
12779+
\pnum
12780+
\expects
12781+
\exposid{current_} is not equal to \exposid{next_}.
12782+
12783+
\pnum
12784+
\returns
12785+
\tcode{subrange(\exposid{current_}, \exposid{next_})}.
12786+
\end{itemdescr}
12787+
12788+
\begin{itemdecl}
12789+
constexpr @\exposid{iterator}@& operator++();
12790+
\end{itemdecl}
12791+
12792+
\begin{itemdescr}
12793+
\pnum
12794+
\expects
12795+
\exposid{current_} is not equal to \exposid{next_}.
12796+
12797+
\pnum
12798+
\effects
12799+
Equivalent to:
12800+
\begin{codeblock}
12801+
@\exposid{current_}@ = @\exposid{next_}@;
12802+
@\exposid{next_}@ = @\exposid{parent_}@->@\exposid{find-next}@(@\exposid{current_}@);
12803+
return *this;
12804+
\end{codeblock}
12805+
\end{itemdescr}
12806+
12807+
\begin{itemdecl}
12808+
constexpr @\exposid{iterator}@ operator++(int);
12809+
\end{itemdecl}
12810+
12811+
\begin{itemdescr}
12812+
\pnum
12813+
\effects
12814+
Equivalent to:
12815+
\begin{codeblock}
12816+
auto tmp = *this;
12817+
++*this;
12818+
return tmp;
12819+
\end{codeblock}
12820+
\end{itemdescr}
12821+
12822+
\begin{itemdecl}
12823+
constexpr @\exposid{iterator}@& operator--() requires @\libconcept{bidirectional_range}@<V>;
12824+
\end{itemdecl}
12825+
12826+
\begin{itemdescr}
12827+
\pnum
12828+
\effects
12829+
Equivalent to:
12830+
\begin{codeblock}
12831+
@\exposid{next_}@ = @\exposid{current_}@;
12832+
@\exposid{current_}@ = @\exposid{parent_}@->@\exposid{find-prev}@(@\exposid{next_}@);
12833+
return *this;
12834+
\end{codeblock}
12835+
\end{itemdescr}
12836+
12837+
\begin{itemdecl}
12838+
constexpr @\exposid{iterator}@ operator--(int) requires @\libconcept{bidirectional_range}@<V>;
12839+
\end{itemdecl}
12840+
12841+
\begin{itemdescr}
12842+
\pnum
12843+
\effects
12844+
Equivalent to:
12845+
\begin{codeblock}
12846+
auto tmp = *this;
12847+
--*this;
12848+
return tmp;
12849+
\end{codeblock}
12850+
\end{itemdescr}
12851+
12852+
\begin{itemdecl}
12853+
friend constexpr bool operator==(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y);
12854+
\end{itemdecl}
12855+
12856+
\begin{itemdescr}
12857+
\pnum
12858+
\returns
12859+
\tcode{x.\exposid{current_} == y.\exposid{current_}}.
12860+
\end{itemdescr}
12861+
12862+
\begin{itemdecl}
12863+
friend constexpr bool operator==(const @\exposid{iterator}@& x, default_sentinel_t);
12864+
\end{itemdecl}
12865+
12866+
\begin{itemdescr}
12867+
\pnum
12868+
\returns
12869+
\tcode{x.\exposid{current_} == x.\exposid{next_}}.
12870+
\end{itemdescr}

source/support.tex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@
672672
#define @\defnlibxname{cpp_lib_ranges}@ 202202L
673673
// also in \libheader{algorithm}, \libheader{functional}, \libheader{iterator}, \libheader{memory}, \libheader{ranges}
674674
#define @\defnlibxname{cpp_lib_ranges_chunk}@ 202202L // also in \libheader{ranges}
675+
#define @\defnlibxname{cpp_lib_ranges_chunk_by}@ 202202L // also in \libheader{ranges}
675676
#define @\defnlibxname{cpp_lib_ranges_iota}@ 202202L // also in \libheader{numeric}
676677
#define @\defnlibxname{cpp_lib_ranges_join_with}@ 202202L // also in \libheader{ranges}
677678
#define @\defnlibxname{cpp_lib_ranges_slide}@ 202202L // also in \libheader{ranges}

0 commit comments

Comments
 (0)