C++20 brings a more powerful iterator system, one of them is to introduce iterator_concept
on the basis of iterator_category
.
I found that the iterator_concept
and iterator_category
of many iterators in C++20 are inconsistent. Take the most famous iota_view
as an example:
using R = decltype(views::iota(0));
static_assert(random_access_range<R>);
using I = ranges::iterator_t<R>;
static_assert(same_as<typename I::iterator_category, input_iterator_tag>);
static_assert(same_as<typename I::iterator_concept, random_access_iterator_tag>);
Although R
models random_access_range
, the iterator_category
of its iterator is just an input_iterator_tag
, which is inconsistent with the iterator_concept
.
Why does C++20 introduce iterator_concept
? What is its purpose? If I implement my own iterator, how do I define iterator_concept
and iterator_category
correctly? Does iterator_category
still have a meaning in C++20?
Iterator_category is an iterator tag function: it is used to determine the category to which an iterator belongs. Specifically, every iterator must belong to a type that is a model of the concept Output Iterator, Input Iterator, Forward Iterator, Bidirectional Iterator, or Random Access Iterator.
iterator_traits are used within algorithms to create local variables of either the type pointed to by the iterator or of the iterator's distance type. The traits also improve the efficiency of algorithms by making use of knowledge about basic iterator categories provided by the iterator_category member.
Forward Iterator is a combination of Bidirectional and Random Access iterator. Therefore, we can say that the forward iterator can be used to read and write to a container. Forward iterators are used to read the contents from the beginning to the end of a container.
Input Iterator is an iterator used to read the values from the container. Dereferencing an input iterator allows us to retrieve the value from the container. It does not alter the value of a container. It is a one-way iterator.
Iterator is an object, which is used to iterate over an iterable object using __next__ () method. Iterators have __next__ () method, which returns the next item of the object. Note that every iterator is also an iterable, but not every iterable is an iterator.
Iterator is an object, which is used to iterate over an iterable object using __next__ () method. Iterators have __next__ () method, which returns the next item of the object. Note that every iterator is also an iterable, but not every iterable is an iterator. For example, a list is iterable but a list is not an iterator.
C++20 introduces a new system of iterators based on concepts that are different from C++17 iterators. While the basic taxonomy remains similar, the requirements for individual iterator categories are somewhat different. C++20 also provides a set of concepts and related utility templates designed to ease constraining common algorithm operations.
The whole list is as given below: Types of iterators: Based upon the functionality of the iterators, they can be classified into five major categories: Input Iterators: They are the weakest of all the iterators and have very limited functionality.
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:
reference
that is either value_type&
or value_type const&
.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:
std::iterator_traits<I>::iterator_category
.std::meow_iterator
conceptsiterator_category
alias and make sure you follow the forward iterator/reference restriction (or... don't, but it's on you)iterator_category
and iterator_concept
type aliases.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With