这是一个 libstdc++ 错误,已提交100639。
iota 是一个非常复杂的范围。特别是,我们需要选择一个足够宽的difference_type,以防止溢出(另请参见P1522)。结果,我们在[range.iota]:
让IOTA-DIFF-T(W)定义如下:
- [...]
- 否则,
IOTA-DIFF-T(W) 是一个有符号整数类型,其宽度大于 W 的宽度(如果存在此类类型)。
- 否则,
IOTA-DIFF-T(W) 是宽度不小于 W 的宽度的未指定的类符号整数类型 ([iterator.concept.winc])。
[注1:未指定此类型是否满足weakly_incrementable。 ——尾注]
对于iota_view<int64_t, int64_t>,我们的差异类型是__int128(一个足够宽的有符号整数类型)。在 gcc 上,signed_integral<__int128> 在以一致性模式 (-std=c++20) 和带有扩展名 (-std=gnu++20) 的 true 编译时为 false。
现在,在 libstdc++ 中,reverse_view 是 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;
// ...
};
这不是reverse_iterator 的指定方式。 [reverse.iterator] 将reference 类型定义为:
using reference = iter_reference_t<Iterator>;
不同的是,后者只是表示*it的类型,而前者实际上是通过iterator_traits,并试图确定reference的含义,如果It::reference不作为一个类型存在。该决定在[iterator.traits] 中指定:
否则,如果 I 满足仅展示概念 cpp17-input-iterator,则 iterator_traits<I> 具有以下可公开访问的成员:[...]
其中reference 是I::reference(如果存在)或iter_reference_t<I>(如果不存在)。这看起来是一样的,但我们必须首先满足cpp17-input-iterator<I>。而cpp17-input-iterator<I> 要求,除其他外:
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>;
};
所以基本上,当且仅当 signed_integral<__int128> 成立时,iterator_t<iota_view<int64_t, int64_t>> 满足 cpp17-input-iterator,这仅在我们在 -std=gnu++20 中编译时才成立。
但我们不需要满足这个要求,因为reverse_iterator<I> 应该直接使用iter_reference_t<I> 而不是通过iterator_traits,这会绕过必须检查signed_integral<__int128>。