1. Introduction
Non-forward Input iterators and output iterators, also known as "Single-pass iterators" are semantically move-only. The standard states:
Note: For input iterators, a == b does not imply ++a == ++b (Equality does not guarantee the substitution property or referential transparency.) Algorithms on input iterators should never attempt to pass through the same iterator twice. They should be single pass algorithms.
This means that once an iterator is copied, only one of the copies can meaningfully be used. Deferencing multiple copies of a single pass iterator often exposes undefined or invalid behavior.
It would, therefore, make sense that classes satisfying the 
Alas, Single-pass iterators and many classes satisfying its requirements predate C++11, they do therefore have move only semantic with copy syntax.
In that regard, they are similar to 
2. Terminology
This paper redefines the requirements of some concepts proposed by the Ranges TS (and the deep merge proposal). In the rest of this paper
- 
     InputIterator InputIterator 
- 
     RangesTSInputIterator InputIterator 
- 
     Cpp17InputIterator 
- 
     OutputIterator OutputIterator 
- 
     RangesTSOutputIterator OutputIterator 
- 
     Cpp17OutputIterator 
3. Scope
This paper proposes changes to the Ranges TS and [P0896R2] both targeting C++20. Because the modifications proposed here changes some requirements and concepts as presented by [P0896R2], the authors strongly suggest they are considered for the inclusion in the same version of the standard. Indeed, [P0896R2] gives us a unique opportunity to make the modifications proposed, as they might, in some cases, break code, if introduced after the publication of C++20 (with ranges).
3.1. Non-Goal
As a large amount of code depends on the Input/Output iterators requirements as specified by C++17, this paper does not propose any modifications to the 
While the ability to use move-only iterators with the algorithms defined in the 
In practice, that means that types satisfying the 
Inversely, types satisfying the 
Because it hardly makes sense to copy an Input Iterator (more on that later), it would be possible to add support for move-only iterators to the 
4. Motivation
4.1. Move-only state
It may be desirable for an iterator to hold a move-only object, becoming itself move-only, which is not possible with iterators modeling Cpp17Iterator.
A real-world example of such iterator is described in [P0902R0].
While syntactically copyable in the current design, a 
4.2. Implicitly destructive operations
Reading from an input sequence is a destructive operation. But that destruction is reflected nowhere in the API.
Less experienced developers may not be aware of the destructive / single-pass nature of non-forward Iterators
By making 
4.3. Performance optimization
Move-only iterators are an optimization opportunity.
For example, in the presence of 
5. What is a move-only iterator?
Unlike [P0902R0], we do not propose to introduce a new iterator category.
A move-only Iterator is a non-forward iterator (either input or output depending on whether is it writable).
This means that a move-only iterator has almost the same semantic requirements as an 
Therefore, this paper does not propose to introduce a new iterator category, new named-requirement, concept name or iterator tag.
Furthermore, there is no 
6. A Holistic Approach to Iterators
While the first part of this paper focuses on making move-only iterators possible, as means to get some code to compile, it is important to take a step back and to think about what movability means for Iterators, from first principles.
An iterator denotes a position into a sequence of elements (whether that sequence maps to memory or not is, for our purpose, irrelevant).
A most basic iterator can be incremented, which means it can move to the next position in the sequence. An iterator does not own the sequence iterated over (there are exceptions, ie: generators), which means the salient property of an iterator is its position in that sequence.
In fact, in Elements of Programming [EOP], an iterator is exactly defined by its distance from the start of the sequence.
Iterators categories then represent the way an iterator can move along that sequence.
- 
     Input and FordwardIterator: sequentially, one direction 
- 
     BidirectionalIterator: sequentially, both directions 
- 
     RandomAccess: both directions in O(1) 
ContiguousIterator is an optimization of RandomAccessIterator specific to the C++ memory model that further, constrain the underlying sequence to be laid out contiguously in memory.
Stepanov theorized an additional category, "Index iterator", which has O(1) access but in a single direction.
Further work was made on iterator categories, notably the Boost.Iterator library
focused on separating traversal (how the iterator moves along the sequence) from
access (whether dereferencing an iterator allows the pointed element to be read, written or both).
While a very interesting concept, it falls outside the scope of this paper.
Just keep in mind that everything that applies to non-forward 
However, focusing on traversal, the set of iterators categories is actually
rather closed, there are only so many ways a sequence can be traversed. An
important point of Stepanov design is that each category is a refinement of the preceeding one. 
So, what separates 
The standard defines the "multi pass" guarantee by stating:
a == b implies ++a == ++b
Given X is a pointer type or the expression (void)++X(a), a is equivalent to the expression a.
In other words:
Two identical objects to which is applied the same transformation are identical.
Copying a 
Which bring us to 
//Given an InputIterator a b = a ; a ++ ; b ; // is invalid. 
However, remember that the sole salient property of an iterator is its distance to the start of the sequence.
Incrementing an iterator only mutates that property (again, conceptually, independently of implementation).
And the only operation that mutates that property is the increment operation (which Stepanov calls 
This implies that as a non-forward iterator moves from one element of the sequence to the next, that element is destroyed.
All of this is well known and is basically rephrasing "Input iterators are single pass".
An important point to make is that how an iterator can traverse a sequence is derived from the nature of the sequence rather than from the iterator itself. The point could be made that there is no such thing as an "Input iterator" Or a "Forward Iterator" because what we really mean is "Iterator over an Input Sequence" or "Iterator over a Forward Sequence".
This is saying that, to be able to reason properly about iterators and traversal, we must assume that the iterator type associated with a sequence is the most specialized possible for that sequence.
The problem is, of course, that we do not have, in the general case, a more meaningful way to express the traversability of a sequence than by defining what type of iterator is used to iterate over it.
It is then the responsibility of the developer providing the sequence to define the most appropriate -- the most specialized -- iterator for that sequence.
In practice, because 
But then, is there a set of operations and semantic requirements, translating to actual C++ syntax, that could allow for
InputIterator to be easily distinguished from each other?
Can we avoid requiring a tag system?
Is there a defining operation that distinguishes 
In fact, there is. We established that 
This model, however, deviates slightly from Stepanov’s work and 
Elements Of Programming has the notion of 
For 
Like a pointer, 
InputIterator i = /*...*/ * i //ok a = i //ok * i //ok i ++ ; // a now invalid 
This design accurately models the nature of iterators. Because an iterator represents a position in a sequence, it is natural that multiple iterators could point to the same position. After one copy is incremented, in Stepanov’s model, other copies are in a partially formed state and cannot be used (but they can be assigned to, or destroyed).
Let’s consider the case where we move from an iterator instead of copying it
InputIterator i = /*...*/ * i //ok a = move ( i ); //ok * i ; //invalid a ++ ; //ok i ++ ; //invalid 
Moving from an iterator invalidates it early, albeit artificially. As per standard, the moved-from iterator is in a valid, but unspecified state, and cannot be used (but can be assigned to, or destroyed). Notice the similarity between "a valid, but unspecified state" and "a partially formed state".
The difference is slim. Notably, both models are equally expressive. References can be used, should multiple names be necessary. In Stepanov’s model iterators are made invalid by the natural mutation of the sequence upon increment rather than by artificially preventing multiple copies.
The second model in which the iterator is moved from, the one we think should be the default way to handle non-forward iterators, is however a much better fit for the C++ model, and offers much stronger guarantees to both the human developer as well as static analysis tools.
In the "increment invalidates" model, objects are spiritually moved-from at a distance, which neither the theory of special relativity nor the C++ memory, model are equipped to handle. This makes it hard for tools to detect invalid uses - although it might become possible with better tools (See Herb Sutter’s CppCon2018 talk). But most concerning, there is no way for a developer to know that the iterators are entangled.
auto i = troubles . begin (); auto schrodingers_iterator = i ; i ++ ; auto nasal_demon = * schrodingers_iterator ; 
The code above might be perfectly fine.
Indeed whether it is well defined or not depends on whether the iterator return by 
Even worse, should the type of 
Moving non-forward iterators, therefore, better expresses intent, is safer and less surprising.
Move-only non-forward Iterators also express the destructive nature of incrementation and give a better sense of the difference between 
6.1. An Holistic Approach to Iterator Tags and Iterator Concepts
Missing the notion of movability pre-c++11 and lacking concepts, 
- 
     Have semantic requirements not expressed through syntax and therefore not enforceable at compile time 
- 
     Need syntax to artificially subscribe to the correct, most refined concept 
Of course, it is not always possible to express all of a type’s semantic requirements through syntax, and in some cases, tags are an unfortunate necessity. However, they should be the mechanism of last recourse, and whenever possible, the semantic requirements should be reflected in the syntax. The idea is that hidden requirements not expressed as code lead to easier-to-misuse types, which inevitably translates to runtime bugs. Ultimately, requirements that can neither be checked at compile time (concepts) or runtime (contracts) are bound to be ignored. Rooted in the belief that not all birds quack like a duck, this proposal leverages meaningful syntactic requirements to increase the type safety of the iterator taxonomy.
In the case of iterators, all requirements of all iterators categories can be expressed syntactically:
template < class I > concept bool InputIterator = Readable < I > && Iterator < I > ; template < class I > concept bool ForwardIterator = InputIterator < I > && Copyable < I > && EqualityComparable < I > ; template < class I > concept bool BidirectionalIterator = ForwardIterator < I > && Decrementable < I > ; template < class I > concept bool RandomAccessIterator = BidirectionalIterator < I > && RandomAccessIncrementable < I > ; template < class I > concept bool ContiguousIterator = BidirectionalIterator < I > && Convertible < I , iter_reference_t < I >> ; 
This is of course simplified but shows that each iterator category subsumes the last and adds a single, cohesive set of requirement enforceable at compile-time. In this design, there is no risk of a type satisfying the wrong concept because of a poorly chosen tag.
6.1.1. Tags as an opt-in opt-out mechanism
Because of a desire to be compatible with the legacy 
Notably, we propose that a type that otherwise meets the requirements of 
template < class I > concept bool ForwardIterator = InputIterator < I > && Copyable < I > && EqualityComparable < I > && ( has_no_iterator_category < I > || DerivedFrom < iterator_category_t < I > , forward_iterator_tag > ); 
7. Q/A
7.1. Non-regular iterators, really?
This proposal advocates for Non-Regular Iterators, and weakens 
However, non-regular types are easier to reason about than types that just pretend to be regular.
Because 
7.2. What about Equality of Input Iterators?
A first, misguided, version of this paper attempted to prevent comparability of types meeting the 
However, preventing 
Early feedback suggested a desire to be able to compare non-forward iterators. Consider the following:
auto a = stream . begin (); auto b = stream . begin (); ... if ( a == b ) { } 
This code will inevitably lead to suffering at some point. However, we cannot prevent people from constructing multiple non-forward iterators, and these iterators will compare equal until one of them invalidate the other.
Two non-forward iterators compare equal if-and-only-if they point to the same position of the same sequence (and only one such position can be referred to at any given time).
Allowing 
7.3. But... Moved-from objects are still objects!
Sure, moving-from leaves a trail of objects in an unspecified state.
However, it is much more easy for tools and humans alike to understand that moved-from objects should
not be used, and in fact, all majors compilers can warn about these patterns.
We think that for the case at hand, focusing on the proper handling of values -- as opposed to objects —
7.4. Does iterators default-constructability needs revisiting?
Default-constructability of iterator seems to have been added, removed and added back to the Ranges TS and the One Ranges Proposal several times. To the best of my knowledge, this was done for the sake of Semiregularity. Given that this proposal strikes semi-regularity, should this question be revisited?
The authors want to point out that default-constructed iterators are almost never in a specified
state and are almost always unsafe to use.
Moreover, DefaultConstructible is not a requirement of any algorithm using ranges and ultimately, we think enforcing
DefaultConstructibility weakens the better 
7.5. What about [P0902R0]?
Andrew Hunter’s "Move-only iterators" paper proposes a design to introduce Move-Only iterators in the taxonomy of 
However, if LEWG feels strongly about a solution compatible with existing algorithms it would be possible
to relax the requirements of concerned algorithms to accept move-only iterators. along with the introduction of a new 
Such algorithms would then be compatible with types satisfying 
If proven with enough confidence that requirements of existing algorithms in the 
So while there would definitively be value in supporting move-only iterators everywhere it makes sense, and the potential for breakage is relatively low, we do not propose it for lack of visibility on the consequences of such changes.
7.6. Why do you want to take my Copyable InputIterators away from me, I like them?!
We do not propose anything of the sort. But, we propose that
- 
     Any InputIterator Copyable ForwardIterator 
- 
     It remains possible to opt-out of that behavior by defining iterator_concept input_iterator_tag 
| Non-copyable Iterator | Copyable Iterator | Copyable Iterator with a tag | 
|---|---|---|
| 
 | 
 | 
 | 
| 
 | 
 | 
 | 
7.7. Will this break existing code ?!
We want to reiterate(!) that all the changes proposed in this paper are only applicable to concepts, types, and requirements of the Ranges TS living
in the 
7.8. Won’t that implicit categorization lead to miss-categorization?
The only valid use cases for 
This proposal is also a teaching opportunity because the nature of 
7.9. Post Increment on non-copyable iterators
Post-incrementing move-only iterators would obviously be incorrect. However, a satisfying solution was offered by [P0541R1].
8. Questions for LEWG
- 
     Does LEWG want to support non-copyable iterators in the ranges 
- 
     Does LEWG agree that non-copyable iterators are always non-forward iterators and do not constitute a new category (ie: no new tag should be introduced)? 
- 
     Does LEWG agree that, in the absence of an explicit tag, a non-copyable iterator that otherwise meets the requirement of InputIterator OutputIterator 
- 
     Does LEWG agree that, in the absence of an explicit tag, a copyable iterator that otherwise meets the requirement of ForwardIterator 
- 
     Does LEWG want to recommend that future non-forward iterators considered for inclusion in the standard should not be copyable? 
- 
     Does LEWG think non-forward views should return begin () 
- 
     Does LEWG agree that ranges :: copy 
- 
     Does LEWG want to revisit the default constructability of iterators given Regular Iterator 
- 
     Does LEWG want to recognize RandomAccessIterator pointer ContiguousIterator 
- 
     Generally, does LEWG support the idea of a tag-less categorization of iterators in the ranges namespace, with tags still supported as an opt-in/opt-out mechanism. 
9. List of proposed changes
Because the ranges-related proposals are still in flux and will require merging multiple documents, we do not provide wording at this time. However, a number of concepts need to be modified in order to allow for iterators that are only movable. This is a departure from the Ranges TS - which itself is grounded in Stepanov work - in which all iterator categories are Regular - or Semi-Regular, which implies copyability.
Note that "ForwardIterator" is defined in terms of its copyability, and so it shall remain regular.
The Copyability, and therefore Regularity of Iterator is therefore moved a few levels down from 
9.1. Changes to <iterator>
9.1.1. WeaklyIncrementable
template < class I > concept WeaklyIncrementable = Movable < I > && requires ( I & i ) { typename iter_difference_t < I > ; { ++ i } -> Same < I &>&& ; i ++ ; }; 
9.1.2. Iterator
9.1.3. InputIterator
Modify the 
template < class I > concept InputIterator = Readable < I > && Iterator < I > ; 
9.1.4. ForwardIterator 
template < class I > concept ForwardIterator = InputIterator && EqualityComparable < I > && Incrementable < I > && ( has_no_iterator_category < I > || DerivedFrom < iterator_category_t < I > , forward_iterator_tag > ) && Sentinel < I , I > ; 
ForwardIterator is made 
9.1.5. OutputIterator
template < class I , class T > concept OutputIterator = Iterator < I > && Writable < I , T > requires ( I & i , T && t ) { * i ++ = std :: forward < T > ( t ); }; 
9.1.6. ContiguousIterator
template < class I , class T > concept OutputIterator = RandomAccessIterator < I > && std :: is_lvalue_reference < iter_reference_t < I >>:: value && Same < iter_value_t < I > , __uncvref < iter_reference_t < I >>> && ConvertibleTo < iter_value_t < I > , I > 
Requiring a conversion operator to 
9.2. Changes to <ranges>
9.2.1. Views
TheSemiRegular View begin () 9.2.2. Inserters
Because the 
It is, therefore, necessary to provide suitable inserters modeling 
9.2.2.1. back_insert_iterator
namespace std :: ranges { template < class Container > class back_insert_iterator : public std :: back_insert_iterator < Container > { public : using std :: back_insert_iterator < Container >:: back_insert_iterator ; back_insert_iterator ( const back_insert_iterator & other ) = delete ; back_insert_iterator ( back_insert_iterator && other ) }; template < class Container > back_insert_iterator < Container > back_inserter ( Container & x ); } 
9.2.2.2. front_insert_iterator
namespace std :: ranges { template < class Container > class front_insert_iterator : public std :: front_insert_iterator < Container > { public : using std :: front_insert_iterator < Container >:: front_insert_iterator ; front_insert_iterator ( const front_insert_iterator & other ) = delete ; front_insert_iterator ( front_insert_iterator && other ); }; template < class Container > front_insert_iterator < Container > front_inserter ( Container & x ); } 
9.2.2.3. insert_iterator
namespace std :: ranges { template < class Container > class insert_iterator : public std :: insert_iterator < Container > { public : using std :: insert_iterator < Container >:: insert_iterator ; insert_iterator ( const insert_iterator & other ) = delete ; insert_iterator ( insert_iterator && other ); }; template < class Container > insert_iterator < Container > inserter ( Container & x , typename Container :: iterator i ); } 
9.3. Changes to <algorithms>
Should algorithms not satisfy 
9.3.1. ranges :: copy 
   In the presence of an InputRange or an 
10. Impact on other proposals
10.1. View proposals
We suggest that the various proposals introducing new non-forward views should have iterators that are neither copyable nor equally comparable.
Non-forward input views can further return 
10.2. Iterator facade
[P0186R0] describes a system for an iterator facade. The changes proposed make defining iterators easier but we think there is still value in an iterator facade. To accommodate and honor the changes proposed here, we suggest that:
- 
     An iterator constructed from a move-only cursor, without an equal ( const cursor & ) InputIterator OutputIterator write 
- 
     An iterator facade constructed from a Copyable cursor with an equal ( const cursor & ) ForwardIterator single_pass = true
10.3. Acknowledgments
The authors like to thank Connor Waters, Tony Van Eerd, Eric Niebler, Casey Carter, Sean Parent and Arthur O’Dwyer who gave tremendously helpful feedbacks during the writing of this paper.