In the C++26-adopted proposal p2542, i.e. std::views::concat
, there is a confusing statement:
The member typedef-name
iterator_category
is defined if and only if all-forward<Const, Views...> is modeled.
That is to say:
- If
concat_view<Views...>
is finally a pure input range, then its iterator type won't contain an inner typeiterator_category
; otherwise,- Its iterator type will contain an inner type
iterator_category
as usual.
What I cannot understand are:
iterator_category
if it's a pure input iterator?The relevant cppref link is here.
std::views::concat::iterator
is not the only iterator not defining iterator_category
. In fact, there are plenty of them and some motivations can be found in
Repairing input range adaptors and counted_iterator
:
1. Abstract
This paper proposes fixes for several issues with
iterator_category
for range and iterator adaptors. This resolves [LWG3283], [LWG3289], and [LWG3408].2. The problem with
iterator_category
This code does not compile:
std::vector<int> vec = {42}; auto r = vec | std::views::transform([](int c) { return std::views::single(c);}) | std::views::join | std::views::filter([](int c) { return c > 0; }); r.begin();
Not because we are breaking any concept requirements:
- the
transform
produces a range of views ofint
;- the
join
takes that and produce an input range ofint
;- the
filter
should then produce another input range ofint…
in theory.The problem is that
join_view
’s iterator for this case has a postfixoperator++
that returnsvoid
, making it not a valid C++17 iterator at all - even C++17 output iterators require*i++
to be valid. In turn, that means thatiterator_traits<join_view::iterator>
is entirely empty, andfilter_view
cannot cope with that as currently specified, because it expectsiterator_category
to be always present (24.7.5.3 [range.filter.iterator]).[LWG3283] and [LWG3289] were discussed at length during during the Belfast and Prague meetings. LWG was aware that as specified the range adaptors do not work with non-C++17 iterators due to these issues. However, I do not believe that it was clear to LWG that this impacts not just the one move-only iterator we have specified in the standard library, but also virtually every range adaptor with its own iterator type when used in conditions that produce an input range.
2.1. The fix
We shouldn’t (and can’t) change postfix increment on the adaptor’s iterators. There’s nothing we can meaningfully return from
operator++
for arbitrary input iterators, especially if we are trying to adapt them. This was discussed in §3.1.2 of [[P0541R1] and there is no need to rehash that discussion here.These iterators are, then, not C++17 iterators at all, [...]
This paper then goes on to describe the fix for counted_iterator
but the reasoning is valid for many iterators, so rather than defining an iterator_category
that doesn't really match the capabilities of the specific iterator and risk having programs relying on those capabilities take the wrong decisions, it's not defined at all.