This paper proposes the
range adaptor family, which takes a range and a function that takes the current element and the current state as parameters. Basically,
is a lazy view version of
or
with a stateful function.
To make common usage of this adaptor easier, this paper also proposes two additional adaptors that further supplement
’s functionality:

, which isviews :: partial_sum
with the binary function defaulted toviews :: scan
.+ 
, which isviews :: prescan
with an initial state seed.views :: scan
The
adaptor is classified as a Tier 1 item in the Ranges plan for C++26 ([P2760R1]).
1. Revision History
1.1. R0 (202407 postSt. Louis Mailing)

Initial revision.
2. Motivation
The motivation for this view is given in [P2760R1] and quoted below for convenience:
If you want to take a range of elements and get a new range that is applying
to every element, that’s
f . But there are many cases where you need a
transform ( f ) that is stateful. That is, rather than have the input to
transform be the current element (and require that
f be
f ), have the input to
regular_invocable be both the current element and the current state.
f For instance, given the range
, if you want to produce the range
[ 1 , 2 , 3 , 4 , 5 ]  you can’t get there with
[ 1 , 3 , 6 , 10 , 15 ] . Instead, you need to use
transform using
scan as the binary operator. The special case of
+ over
scan is partial_sum.
+ One consideration here is how to process the first element. You might want
and you might want
[ 1 , 3 , 6 , 10 , 15 ] (with one extra element); the latter could be called a
[ 0 , 1 , 3 , 6 , 10 , 15 ] .
prescan
This adaptor is also present in rangesv3, where it is called
with the function parameter defaulted to
. However, as [P2760R1] rightfully pointed out,
is probably not suitable for a generic operation like this that can do much more than just calculating a partial sum, similar to how
is not an appropriate name for a general fold. Therefore, the more generic
name is chosen to reflect the nature of this operation. (More discussion on the naming is present in the later sections.)
3. Design
3.1. Why Three Adaptors?
An immediately obvious question is why choose three names instead of opting for a single
adaptor with overloads that take initial seed and/or function parameters? Such a design will look like this (greatly simplified):
struct scan_closure { template < ranges :: input_range Rng , typename T , std :: copy_constructible Fun = std :: plus > requires /* ... */ constexpr operator ()( Rng && rng , const T & init , Func func = {}) { /* ... */ } template < ranges :: input_range Rng , std :: copy_constructible Fun = std :: plus > requires /* ... */ constexpr operator ()( Rng && rng , Func func = {}) { /* ... */ } }; inline constexpr scan_closure scan {};
One immediately obvious problem is that this will definitely cause some confusion to the users due to the fact that a generic name like
defaults to
as its function parameter:
vec  views :: scan // what should this mean?
This is similar to the scenario encountered by
([P2322R6]), where despite the old algorithm,
took
as the default function parameter,
still choose not to have a default. The author feels that the same should be done for
(and introduce a separate
alias that more clearly conveys the intent).
However, even putting aside the function parameter, why cannot the withinitialseed version overload with the usefirstelement version?
Unfortunately, this still does not work due to the same kind of ambiguity that caused
([P2441R2]) to choose a different name instead of overloading with
. Specifically, imagine someone writes a custom
that replicates all the original interfaces but introduces
to mean range concatenation and broadcast:
template < typename T > struct my_vector : public std :: vector < T > { using std :: vector < T >:: vector ; // broadcast: [1, 2, 3] + 10 = [11, 12, 13] friend my_vector operator + ( my_vector vec , const T & value ) { for ( auto & elem : vec ) elem += value ; return vec ; } friend my_vector operator + ( const T & value , my_vector vec ) { /* Same */ } // range concatenation: [1, 2, 3] + [4, 5] = [1, 2, 3, 4, 5] friend my_vector operator + ( my_vector vec , const my_vector & vec2 ) { vec . append_range ( vec2 ); return vec ; } // operator+= implementation omitted };
Although one could argue that this is a misuse of
overloading, this is definitely plausible code one could write. Now consider:
my_vector < int > vec { 1 , 2 , 3 }, vec2 { 4 , 5 }; views :: partial_sum ( vec ); // [1, 3, 6] vec2  views :: partial_sum ( vec ); // [[1, 2, 3], [5, 6, 7], [10, 11, 12]] (!!)
The second invocation,
, is equivalent to
, therefore interpreted as "using
as the initial seed, and add each element of
to it". Unfortunately, we cannot differentiate the two cases since they both invoke
. This ambiguity equally affects
since
and
are also ambiguous.
There are several approaches we can adopt to handle this ambiguity:
Option 1: Bail out. Simply don’t support the initial seed case, or just declare that anything satisfies
that comes in the first argument will be treated as the input range.

Pros: No need to decide on new names.

Cons: Losing a valuable use case that was accustomed by users since
exists. If the "declare" option is chosen, then potentially loses more use case likestd :: exclusive_scan
and causes some confusion.my_vector
Option 2: Reorder arguments and bail out. We can switch the function and the initial seed argument, such that the signature is
, and declare that anything satisfies
that comes in the first argument will be treated as the input range.

Pros: No need to decide on new names.

Cons:

Still has the potential of conflict if a class that is both a range and a binary functor is passed in

Cannot support
with initial seed (discard or need a new name)partial_sum 
Inconsistent argument order with
ranges :: fold

Option 3: Choose separate names for
with and without an initial seed.

Pros: No potential for conflicts

Cons: Need to decide on 12 new names and more wording effort
The author prefers Option 3 as it is the least surprising option that has no potential for conflicts. For now, the author decides that
with an initial seed should be called
, as suggested in [P2760R1], and
should not support an initial seed at all. The rationale for this decision is that people who want
with initial seed can simply call
, so instead of coming up a name that is potentially longer and harder to memorize, the author felt that this is the best approach.
More alternative names are suggested in later sections.
3.2. Prior Art
The scan adaptor/algorithm has made an appearance in many different libraries and languages:

rangev3 has a
adaptor that doesn’t take initial seeds but takes an arbitrary function parameter (defaults toviews :: partial_sum
).+ 
also has an implementation oftl :: ranges
.views :: partial_sum 
Python has an
algorithm that optionally takes initial seeds (with a named argument), and takes an arbitrary function parameter (defaults toitertools . accumulate
).+ 
Furthermore, NumPy provides
algorithm that returns a partial sum (without function parameter), and ancumsum
algorithm that performs arbitrary scans with an arbitrary function parameter that has no default. Neither of those algorithms takes an initial seed.ufunc . accumulate 
Rust has an
adaptor that takes initial seeds and arbitrary function parameters (no defaults provided).iter . scan
The table below summarizes the choices taken by each language and library.
Library  Signature  Function  With Default  Initial Seed 
rangev3 
 ✅  ✅  ❌ 
Python

 ✅  ✅  ✅ 
NumPy 
 ❌  N/A  ❌ 
 ✅  ❌  ❌  
Rust 
 ✅  ❌  ✅ 
Proposed 
 ✅  ❌  ❌ 
 ✅  ❌  ✅  
 ❌  N/A  ❌ 
3.3. Alternative Names
The author thinks
and
are pretty good names. The former has prior examples in
and in Rust, and is a generic enough name that will not cause confusion. The latter also has prior examples in
, and a partial sum is definitely one of the canonical terms used to describe this operation.
Alternative names considered for
(in order of decreasing preference):

(suggested by [P2214R2], and makes the connection withviews :: partial_fold
clear): Pretty good name sinceranges :: fold
is just ascan
with intermediate state saved. However, the correct analogy is actuallyfold
, andranges :: fold_left_first
actually corresponds toranges :: fold_left
, which the author felt may cause some confusion.views :: prescan 
,views :: accumulate
,views :: fold
: The output is a range instead of a number, so using the same name probably is not accurate. The latter two also suffer from the same displaced correspondence problem.views :: fold_left
Alternative names considered for
(in order of decreasing preference):

: Another canonical term for this operation, but doesn’t have prior examples.views :: prefix_sum 
orviews :: cumsum
: Have prior example in NumPy, but in the spirit of Ranges naming we probably need to choose the latter which is a bit long.views :: cumulative_sum
Alternative naming schemes for all 3 or 4 adaptors proposed:
Option A: Name
as
, and
as
. This will make the three adaptors have the same name as the three algorithms already existed in
, which may seem a good idea at first glance. However,
, despite having a generic name, actually requires its function parameter to be associative (or, more precisely, allow for arbitrary order of executing the function on elements), which
or
does not. So, the author felt that reusing the same name may cause some misuse of algorithms.
Option B: Name
as
, and
as
. This will make the naming consistent with the
family but penalize the more common form of withoutinitialseed by making it longer and harder to type. This option also has the advantage of being able to spell
and
as the two forms of partial sums instead of being forced to discard one.
Option C: Keep
, but name
as
(meaning providing an init parameter). Does not penalize the common case but also inconsistent with
. This option also has the advantage of being able to spell
and
as the two forms of partial sums instead of being forced to discard one.
Overall, the author don’t feel any of these options as particularly intriguing, and opt to propose the original naming of
,
and
.
3.4. Left or Right Fold?
Theoretically, there are two possible directions of scanning a range:
// rng = [x1, x2, x3, ...] prescan_left ( rng , i , f ) // [i, f(i, x1), f(f(i, x1), x2), ...] prescan_right ( rng , i , f ) // [i, f(x1, i), f(x2, f(x1, i)), ...]
Both are certainly viable, which begs the question: Should we provide both?
On the one hand,
provided both the left and the right fold version, despite the fact that right fold can be simulated by reversing the range and the function parameter order. However, on the other hand, here the simulation is even easier: just reversing the order of the function parameters will turn a left scan to a right scan.
Furthermore, all of the mentioned prior arts perform left scan, and it is hard to come up with a valid use case of right scan that cannot be easily covered by left scan. Therefore, the author only proposes a left scan in this proposal.
3.5. More Convenience Aliases
Obviously, there are more aliases that can be provided besides
. The three most useful aliases are as follows:
std :: vector < int > vec { 3 , 4 , 6 , 2 , 1 , 9 , 0 , 7 , 5 , 8 } partial_sum ( vec ) // [3, 7, 13, 15, 16, 25, 25, 32, 37, 45] partial_product ( vec ) // [3, 12, 72, 144, 144, 1296, 0, 0, 0, 0] running_min ( vec ) // [3, 3, 3, 2, 1, 1, 0, 0, 0, 0] running_max ( vec ) // [3, 4, 6, 6, 6, 9, 9, 9, 9, 9]
Which are the results of applying
,
,
, and
.
Looking at the results, these are certainly very useful aliases, even as useful as
. However, as stated above, all of these three aliases can be achieved by simply passing the corresponding function object (currently,
/
doesn’t have a function object, but
and
will be useable after [P3136R0] landed) as the function parameter, so I’m not sure it is worth the hassle of specification. Therefore, currently, this proposal does not include the other three aliases, but the author is happy to add them should SG9/LEWG request.
As for
itself, there are several reasons why this alias should be added, which is certainly stronger than the case for
,
, and
:

All existing implementations of scanlike algorithms default the function argument to
whenever there is a default.+ 
existsstd :: partial_sum 
Partial sum is one of the most studied and used concepts in programming, arguably even more useful than running min/max.
3.6. Range Properties
All three proposed views are range adaptors, i.e., they can be piped into.
is just an alias for
, so it will not be mentioned in the below analysis.
3.6.1. Reference and Value Type
Consider the following:
std :: vector < double > vec { 1.0 , 1.5 , 2.0 }; vec  views :: prescan ( 1 , std :: plus {});
Obviously, we expect the result to be
, not
, therefore for
we cannot just use the initial seed’s type as the resulting range’s reference/value type.
There are two choices we can make (for input range type
, function type
, and initial seed type
):

Just use the reference/value type of the input range. This is consistent with
.std :: partial_sum 
Be a bit clever, and use
. In other words, the return type ofremove_cvref_t < invoke_result_t < F & , T & , ranges :: range_reference_t < Rng >>>
.func ( init , * rng . begin ())
Note that the second option doesn’t really cover all kinds of functions since it is entirely plausible that
will change its return type in every invocation. However, this should be enough for nearly all normal cases, and was the decision chosen by [P2322R6] for
. For
, this approach can also work by returning the return type of
.
Although the second choice is somewhat complex in design, it avoids the following pitfalls:
std :: vector < int > vec { 1 , 4 , 2147483647 , 3 }; vec  views :: prescan ( 0L , std :: plus {});
With the first choice, the resulting range’s value type will be
, which would result in UB due to overflow. With the second choice, the resulting value type will be
, which is fine. (Unfortunately, if you used
here, it will still be UB, and that cannot be fixed.)
Given that
chose the second approach, the author for now also choose the second approach. I can be convinced otherwise though.
3.6.2. Category
At most forward.
In other words, forward if the input range is forward, otherwise input. The resulting range cannot be bidirectional since the function parameter cannot be applied in reverse. A future extension may enable a
with both forward and backward function parameters, but that is outside the scope of this paper.
3.6.3. Common
Never.
This is consistent with rangev3’s implementation. The reasoning is that each iterator needs to store both the current position and the current partial sum (generalized) so that the
position’s iterator is not readily available in O(1) time.
3.6.4. Sized
If and only if the input range is sized.
For
, the size is always equal to the input range’s size. For
, the size is always equal to the input range’s size plus one.
3.6.5. ConstIterable
Similar to
, if and only if the input range is constiterable and
is
invocable.
3.6.6. Borrowed
Never.
At least for now. Currently,
is never borrowed, but after [P3117R0] determined suitable criteria for storing the function parameter in the iterator, it can be conditionally borrowed.
Theoretically, the same can be applied to
to make it conditionally borrowed by storing the function and the initial value inside the iterator. However, the author would like to wait until [P3117R0] lands to make this change.
3.7. Feature Test Macro
This proposal added a new feature test macro
, which signals the availability of all three adaptors.
An alternate design is to introduce 3 macros, but the author felt such granularity is not needed.
3.8. Freestanding
[P1642R11] included nearly everything in
into freestanding, except
and the corresponding view types. However, range adaptors added after [P1642R11], like
and
did not include themselves in freestanding.
The author assumes that this is an oversight, since I cannot see any reason for those views to not be in freestanding. As a result, the range adaptor proposed by this paper will be included in freestanding. (Adding freestanding markers to the two views mentioned above is probably out of the scope of this paper, but if LWG decides that this paper should also solve that oversight, the author is happy to comply.)
4. Implementation Experience
The author implemented this proposal in Compiler Explorer. No significant obstacles are observed.
Note that to save on implementation effort,
is simply aliased to
, so they both share a single underlying view.
5. Wording
The wording below is based on [N4981].
Wording notes for LWG and editor:

The wording currently reflects Option 3 from the why three adaptors section, with name as
,views :: scan
andprescan
.partial_sum 
andviews :: scan
use the same underlyingviews :: prescan
to save on complexity and duplication of wording.ranges :: scan_view 
Currently, the three views' definitions reside just after
’s definition and synopsis due to the author’s perception that they are pretty similar.views :: transform
Wording questions to be resolved:

Should the view be called
orscan_view
?prescan_view 
Currently, the current sum is cached in the iterator object (as implemented in rangev3). An alternative is to cache the current sum in the view object (as implemented in
) such that each iterator only needs to hold a pointer to the parent view and an iterator to the current position. Which one should be chosen?tl :: ranges 
Two strategy exist to defer
toscan
. First (used currently) is to define two constructors forprescan
that takes two and three arguments, and have one constructor delegates to the other. The other approach is to specify thatscan_view
is expressionequivalent toscan ( E , F )
.scan_view ( E , * ranges :: begin ( E ), F ) 
Due to never being a common range,
simply has a singlescan_view
member that returnsend () const
. Is that the correct way to do this, or should I write two differentdefault_sentinel
functions for both cases, with different constraints, but both returnend ()
? Or should I actually write adefault_sentinel
subclass that wraps the iterator?sentinel < Const > 
The extra
parameter andbool IsInit
requirement seem a bit clunkyassignable_from
5.1. 17.3.2 Header < version >
synopsis [version.syn]
In this clause’s synopsis, insert a new macro definition in a place that respects the current alphabetical order of the synopsis, and substituting
by the date of adoption.
#define __cpp_lib_ranges_scan 20XXYYL // freestanding, also in <ranges>
5.2. 26.2 Header < ranges >
synopsis [ranges.syn]
Modify the synopsis as follows:
// [...] namespace std :: ranges { // [...] // [range.transform], transform view template < input_range V , move_constructible F , bool IsInit > requires view < V > && is_object_v < F > && regular_invocable < F & , range_reference_t < V >> && can  reference < invoke_result_t < F & , range_reference_t < V >>> class transform_view ; // freestanding namespace views { inline constexpr unspecified transform = unspecified ; } // freestanding // [range.scan], scan view template < input_range V , typename T , move_constructible F > requires view < V > && is_object_v < T > && is_object_v < F > && regular_invocable < F & , T & , range_reference_t < V >> && assignable_from < remove_cvref_t < invoke_result_t < F & , T & , range_reference_t < V >>>& , T &> && can  reference < invoke_result_t < F & , T & , range_reference_t < V >>> class scan_view ; // freestanding namespace views { inline constexpr unspecified scan = unspecified ; // freestanding inline constexpr unspecified prescan = unspecified ; // freestanding inline constexpr unspecified prefix_sum = unspecified ; // freestanding } // [range.take], take view template < view > class take_view ; // freestanding template < class T > constexpr bool enable_borrowed_range < take_view < T >> = enable_borrowed_range < T > ; // freestanding namespace views { inline constexpr unspecified take = unspecified ; } // freestanding // [...] }
Editor’s Note: Add the following subclause to 26.7 Range adaptors [range.adaptors], after 26.7.9 Transform view [range.transform]
5.3. 26.7.� Scan view [range.scan]
5.3.1. 26.7.�.1 Overview [range.scan.overview]

presents a view that accumulates the results of applying a transformation function to the current state and each element.scan_view 
The name
denotes a range adaptor object ([range.adaptor.object]). Given subexpressionsviews :: scan
andE
, the expressionF
is expressionequivalent toviews :: scan ( E , F )
.scan_view ( E , F )
[Example 1:
vector < int > vec { 1 , 2 , 3 , 4 , 5 }; for ( auto && i : std :: views :: scan ( vec , std :: plus {})) { std :: ( "{} " , i ); // prints 1 3 6 10 15 }
 end example]

The name
denotes a range adaptor object ([range.adaptor.object]). Given subexpressionsviews :: prescan
,E
andF
, the expressionG
is expressionequivalent toviews :: prescan ( E , F , G )
.scan_view ( E , F , G )
[Example 2:
vector < int > vec { 1 , 2 , 3 , 4 , 5 }; for ( auto && i : std :: views :: prescan ( vec , 10 , std :: plus {})) { std :: ( "{} " , i ); // prints 10 11 13 16 20 25 }
 end example]

The name
denotes a range adaptor object ([range.adaptor.object]). Given subexpressionsviews :: partial_sum
, the expressionE
is expressionequivalent toviews :: partial_sum ( E )
.scan_view ( E , std :: plus {})
5.3.2. 26.7.�.2 Class template scan_view
[range.scan.view]
namespace std :: ranges { template < input_range V , typename T , move_constructible F , bool IsInit = false> requires view < V > && is_object_v < T > && is_object_v < F > && regular_invocable < F & , T & , range_reference_t < V >> && assignable_from < remove_cvref_t < invoke_result_t < F & , T & , range_reference_t < V >>>& , T &> && can  reference < invoke_result_t < F & , T & , range_reference_t < V >>> class scan_view : public view_interface < scan_view < V , T , F , IsInit >> { private : // [range.scan.iterator], class template scan_view::iterator template < bool > struct iterator ; // exposition only V base_ = V (); // exposition only non  propagating  cache < T > init_ ; // exposition only movable  box < F > fun_ ; // exposition only public : scan_view () requires default_initializable < V > && default_initializable < F > = default ; constexpr explicit scan_view ( V base , F fun ) requires ( ! IsInit ); constexpr explicit scan_view ( V base , const T & init , F fun ) requires IsInit ; constexpr V base () const & requires copy_constructible < V > { return base_ ; } constexpr V base () && { return std :: move ( base_ ); } constexpr iterator < false> begin (); constexpr iterator < true> begin () const requires range < const V > && regular_invocable < const F & , const T & , range_reference_t < const V >> && assignable_from < remove_cvref_t < invoke_result_t < const F & , const T & , range_reference_t < const V >>>& , const T &> ; constexpr default_sentinel_t end () const { return default_sentinel ; } constexpr auto size () requires sized_range < V > { return ranges :: size ( base_ ) + ( IsInit ? 1 : 0 ); } constexpr auto size () const requires sized_range < const V > { return ranges :: size ( base_ ) + ( IsInit ? 1 : 0 ); } }; template < class R , class F > scan_view ( R && , F ) > scan_view < views :: all_t < R > , range_value_t < R > , F , false> ; template < class R , class T , class F > scan_view ( R && , T , F ) > scan_view < views :: all_t < R > , T , F , true> ; }
constexpr explicit scan_view ( V base , F fun ) requires ( ! IsInit );

Effects: Initializes
withbase_
andstd :: move ( base )
withfun_
.std :: move ( fun )
constexpr explicit scan_view ( V base , const T & init , F fun ) requires IsInit ;

Effects: Initializes
withbase_
,std :: move ( base )
withinit_
, andinit
withfun_
.std :: move ( fun )
constexpr iterator < false> begin ();

Effects: Equivalent to:
return iterator < false> { * this , ranges :: begin ( base_ )};
constexpr iterator < true> begin () const requires range < const V > && regular_invocable < const F & , const T & , range_reference_t < const V >> ;

Effects: Equivalent to:
return iterator < true> { * this , ranges :: begin ( base_ )};
5.3.3. 26.7.�.3 Class template scan_view :: iterator
[range.scan.iterator]
namespace std :: ranges { template < input_range V , typename T , move_constructible F , bool IsInit > requires view < V > && is_object_v < T > && is_object_v < F > && regular_invocable < F & , T & , range_reference_t < V >> && assignable_from < remove_cvref_t < invoke_result_t < F & , T & , range_reference_t < V >>>& , T &> && can  reference < invoke_result_t < F & , T & , range_reference_t < V >>> template < bool Const > class scan_view < V , T , F , IsInit >:: iterator { private : using Parent = maybe  const < Const , scan_view > ; // exposition only using Base = maybe  const < Const , V > ; // exposition only using RefType = invoke_result_t < maybe  const < Const , F >& , maybe  const < Const , T >& , range_reference_t < Base >> ; // exposition only using SumType = remove_cvref_t < RefType > ; // exposition only iterator_t < Base > current_ = iterator_t < Base > (); // exposition only Parent * parent_ = nullptr ; // exposition only movable  box < SumType > sum_ ; // exposition only bool is_init_ = false; // exposition only public : using iterator_concept = conditional_t < forward_range < Base > , forward_iterator_tag , input_iterator_tag > ; using iterator_category = see below ; // present only if Base models forward_range using value_type = SumType ; using difference_type = range_difference_t < Base > ; iterator () requires default_initializable < iterator_t < Base >> = default ; constexpr iterator ( Parent & parent , iterator_t < Base > current ); constexpr iterator ( iterator <! Const > i ) requires Const && convertible_to < iterator_t < V > , iterator_t < Base >> ; constexpr const iterator_t < Base >& base () const & noexcept ; constexpr iterator_t < Base > base () && ; constexpr RefType operator * () const { return * sum_ ; } constexpr iterator & operator ++ (); constexpr void operator ++ ( int ); constexpr iterator operator ++ ( int ) requires forward_range < Base > ; friend constexpr bool operator == ( const iterator & x , const iterator & y ) requires equality_comparable < iterator_t < Base >> ; friend constexpr bool operator == ( const iterator & x , default_sentinel_t ); }; }

If
does not modelBase
there is no memberforward_range
. Otherwise, the typedefnameiterator_category
denotes:iterator_category

ifforward_iterator_tag
modelsiterator_traits < iterator_t < Base >>:: iterator_category
andderived_from < forward_iterator_tag >
isis_reference_v < invoke_result_t < maybe  const < Const , F >& , maybe  const < Const , T >& , range_reference_t < Base >>> true
; 
otherwise,
.iterator_traits < iterator_t < Base >>:: iterator_category
constexpr iterator ( Parent & parent , iterator_t < Base > current );

Effects: Initializes
withcurrent_
,std :: move ( current )
withparent_
, andaddressof ( parent )
withis_init_
. Then, equivalent to:IsInit
if constexpr ( IsInit ) { sum_ = * parent_ > init_ ; } else { if ( current_ != ranges :: end ( parent_ > base_ )) { sum_ = * current_ ; } }
constexpr iterator ( iterator <! Const > i ) requires Const && convertible_to < iterator_t < V > , iterator_t < Base >> ;

Effects: Initializes
withcurrent_
,std :: move ( i . current_ )
withparent_
,i . parent_
withsum_
, andstd :: move ( i . sum_ )
withis_init_
.i . is_init_
constexpr const iterator_t < Base >& base () const & noexcept ;

Effects: Equivalent to:
return current_ ;
constexpr iterator_t < Base > base () && ;

Effects: Equivalent to:
return std :: move ( current_ );
constexpr iterator & operator ++ ();

Effects: Equivalent to:
if ( ! is_init_ ) { ++ current_ ; } else { is_init_ = false; } if ( current_ != ranges :: end ( parent_ > base_ )) { sum_ = invoke ( * parent_ > fun_ , * sum_ , * current_ ); } return * this ;
constexpr void operator ++ ( int );

Effects: Equivalent to
.++* this
constexpr iterator operator ++ ( int ) requires forward_range < Base > ;

Effects: Equivalent to:
auto tmp = * this ; ++* this ; return tmp ;
friend constexpr bool operator == ( const iterator & x , const iterator & y ) requires equality_comparable < iterator_t < Base >> ;

Effects: Equivalent to:
return x . current_ == y . current_ ;
friend constexpr bool operator == ( const iterator & x , default_sentinel_t );

Effects: Equivalent to:
return x . current_ == ranges :: end ( x . parent_ > base_ );