The standard defines a variety of range adaptors in [range.adaptors], and some of them have their own iterator types.
In order to standardize the iterator_category
of these iterators, the standard also specifies how they are defined. For example, in [range.transform.iterator-2], the iterator_category
of transform_view::iterator
is defined as follows:
The member typedef-name
iterator_category
is defined if and only ifBase
modelsforward_range
. In that case,iterator::iterator_category
is defined as follows: LetC
denote the typeiterator_traits<iterator_t<Base>>::iterator_category
.If
is_lvalue_reference_v<invoke_result_t<F&, range_reference_t<Base>>>
istrue
, then
if
C
modelsderived_from<contiguous_iterator_tag>
,iterator_category
denotesrandom_access_iterator_tag
;otherwise,
iterator_category
denotesC
.Otherwise,
iterator_category
denotesinput_iterator_tag
.
But this definition seems to have some problems, consider the following case:
vector v{1, 2, 3, 4, 5};
auto r = views::iota(0, 5) |
views::transform([&](int i) -> int& { return v[i]; });
using I = ranges::iterator_t<decltype(r)>;
static_assert(random_access_iterator<I>);
static_assert(same_as<I::iterator_concept, random_access_iterator_tag>);
static_assert(__detail::__cpp17_randacc_iterator<I>);
static_assert(same_as<I::iterator_category, input_iterator_tag>);
Since r
is perfect random_access_range
in C++20, its iterator models random_access_iterator
, however, according to the description above, since iota_view::iterator_category
is only input_iterator_tag
, the iterator_category
of iterator I
is also only input_iterator_tag
.
But obviously, iterator I
meets all the requirements of LegacyRandomAccessIterator
, so it should be more reasonable if its iterator_category
is random_access_iterator
.
Is this a standard defect? Or is there a consideration behind this?