This is a libstdc++ bug, submitted 100639.
iota
is a surprisingly complex range. In particular, we need to pick a difference_type
that is sufficiently wide for the type that we're incrementing to avoid overflow (see also P1522). As a result, we have in [range.iota]:
Let IOTA-DIFF-T(W)
be defined as follows:
- [...]
- Otherwise,
IOTA-DIFF-T(W)
is a signed integer type of width greater than the width of W
if such a type exists.
- Otherwise,
IOTA-DIFF-T(W)
is an unspecified signed-integer-like type ([iterator.concept.winc]) of width not less than the width of W
.
[Note 1: It is unspecified whether this type satisfies weakly_incrementable
. — end note]
For iota_view<int64_t, int64_t>
, our difference type is __int128
(a wide-enough signed integral type). On gcc, signed_integral<__int128>
is false
when compiling in conforming mode (-std=c++20
) and true
with extensions (-std=gnu++20
).
Now, in libstdc++, reverse_view
is implemented as:
template<typename _Iterator>
class reverse_iterator
: public iterator<typename iterator_traits<_Iterator>::iterator_category,
typename iterator_traits<_Iterator>::value_type,
typename iterator_traits<_Iterator>::difference_type,
typename iterator_traits<_Iterator>::pointer,
typename iterator_traits<_Iterator>::reference>
{
// ...
typedef typename __traits_type::reference reference;
// ...
_GLIBCXX17_CONSTEXPR reference operator*() const;
// ...
};
This isn't how reverse_iterator
is specified. [reverse.iterator] defines the reference
type as:
using reference = iter_reference_t<Iterator>;
The difference is that the latter just means the type of *it
, while the former actually goes through iterator_traits
and tries to determine what reference
means if It::reference
doesn't exist as a type. That determination is specified in [iterator.traits]:
Otherwise, if I
satisfies the exposition-only concept cpp17-input-iterator
, iterator_traits<I>
has the following publicly accessible members: [...]
where reference
is I::reference
if it exists or iter_reference_t<I>
if it doesn't. That looks like it's the same thing, but we have to first satisfy cpp17-input-iterator<I>
. And cpp17-input-iterator<I>
requires, among other things:
template<class I>
concept cpp17-input-iterator =
cpp17-iterator<I> && equality_comparable<I> && requires(I i) {
// ...
requires signed_integral<typename incrementable_traits<I>::difference_type>;
};
So basically, iterator_t<iota_view<int64_t, int64_t>>
satsifies cpp17-input-iterator
if and only if signed_integral<__int128>
holds, which is only true if we're compiling in -std=gnu++20
.
But we shouldn't need to meet this requirement, since reverse_iterator<I>
should just directly use iter_reference_t<I>
and not go through iterator_traits
, which side-steps having to check signed_integral<__int128>
.