【问题标题】:Why does views::reverse not work with iota_view<int64_t, int64_t>为什么 views::reverse 不适用于 iota_view<int64_t, int64_t>
【发布时间】:2021-08-06 22:17:43
【问题描述】:

我有following C++ program,但由于某种原因,我不能使用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 编译。

【问题讨论】:

标签: c++ c++20 std-ranges


【解决方案1】:

这是一个 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&lt;int64_t, int64_t&gt;,我们的差异类型是__int128(一个足够宽的有符号整数类型)。在 gcc 上,signed_integral&lt;__int128&gt; 在以一致性模式 (-std=c++20) 和带有扩展名 (-std=gnu++20) 的 true 编译时为 false

现在,在 libstdc++ 中,reverse_viewimplemented 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&lt;I&gt; 具有以下可公开访问的成员:[...]

其中referenceI::reference(如果存在)或iter_reference_t&lt;I&gt;(如果不存在)。这看起来是一样的,但我们必须首先满足cpp17-input-iterator&lt;I&gt;。而cpp17-input-iterator&lt;I&gt; 要求,除其他外:

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&lt;__int128&gt; 成立时,iterator_t&lt;iota_view&lt;int64_t, int64_t&gt;&gt; 满足 cpp17-input-iterator,这仅在我们在 -std=gnu++20 中编译时才成立。

但我们不需要满足这个要求,因为reverse_iterator&lt;I&gt; 应该直接使用iter_reference_t&lt;I&gt; 而不是通过iterator_traits,这会绕过必须检查signed_integral&lt;__int128&gt;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-21
    • 2011-06-17
    • 1970-01-01
    • 2016-11-05
    相关资源
    最近更新 更多