问题描述
我有 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
编译。
解决方法
这是一个 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>
在符合模式 (false
) 下编译时为 -std=c++20
,true
为扩展名 (-std=gnu++20
)。
现在,在 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
不意味着什么' t 作为一种类型存在。该决定在 [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>;
};
所以基本上,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
,这需要检查{{1} }.