为什么视图::反向不适用于iota_view<int64_t,int64_t>

Why does views::reverse not work with iota_view<int64_t, int64_t>

本文关键字:int64 lt gt 为什么 不适用 适用于 视图 iota view      更新时间:2024-09-26

我有以下C++程序,由于某种原因我不能使用int64_t作为模板参数。

#include <iostream>
#include <ranges>
template<typename T> 
void fn() {
for (auto val : std::ranges::iota_view{T{1701}, T{8473}} 
| std::views::reverse
| std::views::take(5))
{
std::cout << val << std::endl;
}
}
int main()
{
fn<int16_t>();
fn<int32_t>();
// does not compile:
// fn<int64_t>();
}

这是意料之中的(我做错了什么),还是只是编译器/标准库中的一些不幸的错误?

注意:当我删除std::views::reverse代码时,也会为int64_t编译。

这是一个libstdc++错误,100639提交。

<小时 />

iota是一个令人惊讶的复杂范围。特别是,我们需要选择一个足够宽的difference_type,以适应我们要递增的类型以避免溢出(另请参阅 P1522)。因此,我们在 [range.iota] 中有:

让我们IOTA-DIFF-T(W)定义如下:

  • [...]
  • 否则,IOTA-DIFF-T(W)是宽度大于W宽度的有符号整数类型(如果存在此类类型)。
  • 否则,IOTA-DIFF-T(W)是一个未指定的有符号整数类类型([iterator.concept.winc]),其宽度不小于W的宽度。

[注1:未指定此类型是否满足weakly_­incrementable— 尾注]

对于iota_view<int64_t, int64_t>,我们的差分类型是__int128(一个足够宽的有符号积分类型)。在 gcc 上,signed_integral<__int128>在以符合模式 (-std=c++20) 编译并使用扩展 (-std=gnu++20truefalse

现在,在libstdc++中,reverse_view实现为:

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-iteratoriterator_­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>;
};

所以基本上,iterator_t<iota_view<int64_t, int64_t>>满足cpp17-input-iterator当且仅当signed_integral<__int128>成立,这只有在我们在-std=gnu++20中编译时才是正确的。

但是我们不需要满足这个要求,因为reverse_iterator<I>应该直接使用iter_reference_t<I>而不是经过iterator_traits,这回避了必须检查signed_integral<__int128>的步骤。