如何解决为什么 views::reverse 可以将 non-sized_range 转换为 size_range?
sized_range
概念细化了范围,要求
范围内的元素数量可以在摊销中确定
恒定时间使用ranges::size
。
template<class T>
concept sized_range =
range<T> &&
requires(T& t) { ranges::size(t); };
标准规定获得ranges::sized_range
的大小保证在恒定时间内。考虑以下几点:
auto r1 = std::views::iota(0)
| std::views::filter([](int x){ return x % 2 == 0; })
| std::views::take(1'000'000);
r1
显然不是 sized_range
,因为不可能在
常数时间,这也意味着我们使用 ranges::size
来评估其大小也是格式错误的。
但是我偶然发现如果我们在它上面应用views::reverse
,新的范围r2
突然变成了sized_range
,我们可以直接使用ranges::size
得到它的大小正确,godbolt:
auto r2 = r1 | views::reverse;
static_assert(!ranges::sized_range<decltype(r1)>);
static_assert( ranges::sized_range<decltype(r2)>);
std::cout << std::ranges::size(r2) << "\n"; // print 500'000
然而,很明显,新范围 r2
不是 sized_range
,因为我们永远无法在恒定时间内获得它的大小,这似乎违反了标准所说的。
为什么 views::reverse
可以将非sized_range
转换为 sized_range
?显然,这种转换不会对原始范围的大小产生任何影响。这是标准缺陷还是库错误?
解决方法
要求是摊销恒定的,并不总是恒定的。
-
take_view<...>
产生counted_iterator
。 - 所以
reverse_view<take_view<...>>
产生reverse_iterator<counted_iterator<...>>
-
counted_iterator
始终可以减去:您只需减去计数即可。 - 所以
reverse_iterator<counted_iterator<...>>
也总是可以减去。 -
ranges::size
是为迭代器/哨兵模型sized_sentinel_for
的任何范围定义的。这包括reverse_view<take_view<...>>
。
为了满足分摊的常数复杂度要求,reverse_view::begin
会在需要计算源范围的末尾时缓存它(即源范围不常见)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。