为什么 views::reverse 不适用于 iota_view<int64_t, int64_t>

NoS*_*tAl 5 c++ c++20 std-ranges

我有以下 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>();
}
Run Code Online (Sandbox Code Playgroud)

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

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

Bar*_*rry 8

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


iota是一个令人惊讶的复杂范围。特别是,我们需要为difference_type我们正在递增的类型选择足够宽的a以避免溢出(另见P1522)。结果,我们在[range.iota] 中有

IOTA-DIFF-T(W)被定义如下:

  • [...]
  • 否则,IOTA-DIFF-T(W)是宽度大于宽度的有符号整数类型(W如果存在此类类型)。
  • 否则,IOTA-DIFF-T(W)是宽度不小于W.

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

对于iota_view<int64_t, int64_t>,我们的差分类型是__int128(一个足够宽的有符号整数类型)。在 gcc 上,signed_integral<__int128>false在符合模式 ( -std=c++20) 和true扩展名 ( -std=gnu++20)下编译时。

现在,在的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;
  // ...
};
Run Code Online (Sandbox Code Playgroud)

这不是如何reverse_iterator指定的。[reverse.iterator]reference类型定义为:

using reference = iter_reference_t<Iterator>;
Run Code Online (Sandbox Code Playgroud)

不同之处在于后者仅表示 的类型*it,而前者实际上经过iterator_traits并尝试确定referenceifIt::reference不作为类型存在是什么意思。该决定在[iterator.traits] 中指定:

否则,如果I满足 exposition-only 概念cpp17-input-iterator,则iterator_­traits<I>具有以下可公开访问的成员:[...]

这里referenceI::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>;
  };
Run Code Online (Sandbox Code Playgroud)

所以基本上,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>