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?
According to C++20, the iterator category for a transform_view
is determined as follows :
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>>>
is true, then
- if
C
modelsderived_from<contiguous_iterator_tag>
,iterator_category
denotesrandom_access_iterator_tag
;- otherwise,
iterator_category
denotesC
.- Otherwise,
iterator_category
denotesinput_iterator_tag
.
So if your functor returns an lvalue reference, then the iterator category is derived from the iterator category of the base iterator type. And the base range that provides the base iterator is views::iota
.
However, because iota
is a prvalue range (its iterators return prvalues, not references), its iterator_category
cannot be anything other than std::input_iterator_tag
. So in the above text for transform_view
, C
will be std::input_iterator_tag
. So the category for transform_view
will be std::input_iterator_tag
, no matter what your functor returns.
You should really avoid caring about iterator_category
when you're using std::ranges
-based code. If you know you're dealing with C++20 ranges, then you should be using the iterator_concept
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.