There are differences between the C++17 (C++98) iterator model and the C++20 Ranges iterator model that are not backwards compatible. The two big ones are:
- The C++98 model requires that forward iterators have a
reference
that is either value_type&
or value_type const&
.
- The C++98 model does not allow for
contiguous
iterators. The strongest category was random_access
.
The consequence of (1) is pretty significant - it means that if you have an iterator that returns a prvalue (whether proxy reference or not), it can never be stronger than an input iterator. So, views::iota(1, 10)
, despite easily being able to support random access, is only, at best, a C++98 input range.
However, you can't just... remove this requirement. Existing code that assumes C++98 iterators and uses iterator_category
to make judgements is perfectly within its rights to assume that if iterator_category
is, say, bidirectional_iterator_tag
, that its reference
is some kind of lvalue reference to value_type
.
What iterator_concept
does is add a new C++20 layer that allows an iterator to both advertise its C++98/17 category and, distinctly, advertise its C++20 category. So going back to the iota_view<int, int>
example, that view's iterator has iterator_category
set to input_iterator_tag
(because the reference
is a prvalue and so it does not satisfy the old requirements for even forward) but its iterator_concept
is set to random_access_iterator_tag
(because once we drop that restriction, we can easily support all the random access restrictions).
In [iterator.concepts.general], we have this magic function ITER_CONCEPT(I)
which helps us figure out what tag to use in C++20.
The issue with (2) is that it was hard to just add a new contiguous_iterator_tag
before due to the way that various C++98/17 code would check for that tag (lots of code might check for exactly random_access_iterator_tag
). The iterator_concept
approach avoids this problem by also introducing concepts that directly check the right thing for you (i.e. the random_access_iterator
concept checks that ITER_CONCEPT(I)
derives from random_access_iterator_tag
, not that it simply is that).
Guidelines:
- If you're using an iterator in C++17, use
std::iterator_traits<I>::iterator_category
.
- If you're using an iterator in C++20, use the
std::meow_iterator
concepts
- If you're writing an iterator in C++17, add the
iterator_category
alias and make sure you follow the forward iterator/reference restriction (or... don't, but it's on you)
- If you're writing an iterator in C++20, follow the guidance in P2259 which has a good description of the problem and how and when to provide the
iterator_category
and iterator_concept
type aliases.