为什么 views::reverse 不适用于 iota_view<int64_t, int64_t>
Why does views::reverse not work with iota_view<int64_t, int64_t>
我有 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>();
}
这是预期的(我做错了什么),还是 compiler/std 库中的一些不幸错误?
注意:当我删除 std::views::reverse
代码时,也会为 int64_t
编译。
这是一个 libstdc++ 错误,已提交 100639。
iota
是一个非常复杂的范围。特别是,我们需要选择一个 difference_type
,它对于我们正在递增的类型来说足够宽,以避免溢出(另请参见 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]
对于iota_view<int64_t, int64_t>
,我们的差异类型是__int128
(一个足够宽的有符号整数类型)。在 gcc 上,signed_integral<__int128>
在符合模式 (-std=c++20
) 下编译时为 false
,在扩展 (-std=gnu++20
) 下为 true
。
现在,在 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]:
中指定
Otherwise, if I
satisfies the exposition-only concept cpp17-input-iterator
, iterator_traits<I>
has the following publicly accessible members: [...]
其中 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
.[=58= 中编译时才是正确的]
但我们不需要满足这个要求,因为 reverse_iterator<I>
应该直接使用 iter_reference_t<I>
而不是通过 iterator_traits
,这需要检查 signed_integral<__int128>
.
我有 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>();
}
这是预期的(我做错了什么),还是 compiler/std 库中的一些不幸错误?
注意:当我删除 std::views::reverse
代码时,也会为 int64_t
编译。
这是一个 libstdc++ 错误,已提交 100639。
iota
是一个非常复杂的范围。特别是,我们需要选择一个 difference_type
,它对于我们正在递增的类型来说足够宽,以避免溢出(另请参见 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 ofW
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 ofW
.[Note 1: It is unspecified whether this type satisfies
weakly_incrementable
. — end note]
对于iota_view<int64_t, int64_t>
,我们的差异类型是__int128
(一个足够宽的有符号整数类型)。在 gcc 上,signed_integral<__int128>
在符合模式 (-std=c++20
) 下编译时为 false
,在扩展 (-std=gnu++20
) 下为 true
。
现在,在 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]:
Otherwise, if
I
satisfies the exposition-only conceptcpp17-input-iterator
,iterator_traits<I>
has the following publicly accessible members: [...]
其中 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
.[=58= 中编译时才是正确的]
但我们不需要满足这个要求,因为 reverse_iterator<I>
应该直接使用 iter_reference_t<I>
而不是通过 iterator_traits
,这需要检查 signed_integral<__int128>
.