如何解决为什么`iota(0) | take(0)` 不是 C++20 中的模型范围::sized_range?
考虑以下代码片段:
#include <ranges>
auto r = std::views::iota(0) | std::views::take(0);
static_assert(std::ranges::sized_range<decltype(r)>);
gcc-trunk rejects 它对于 required-expression std::ranges::size(r)
是无效的。为什么 r
不能模拟 ranges::sized_range
,即为什么我不能在它上面使用 std::ranges::size
?
更新
使用 range-v3 后编译。 C++23 需要此功能,还是 LWG 问题?
#include <range/v3/all.hpp>
#include <ranges>
auto r = ranges::views::iota(0) | ranges::views::take(0);
static_assert(std::ranges::sized_range<decltype(r)>);
问题似乎是sentinel_t
中r
的{{1}}只是满足std::sized_sentinel_for<ranges::counted_iterator>
的range-v3
,因为有一个有效的{{ 1}} 在 [predef.iterators#iterators.counted] 中用于这两种类型:
ranges::default_sentinel
但是在operator-
中,friend constexpr iter_difference_t<I> operator-(
const counted_iterator& x,default_sentinel_t);
friend constexpr iter_difference_t<I> operator-(
default_sentinel_t,const counted_iterator& y);
的{{1}}是namepace std::ranges
,不能转换为sentinel_t
。
解决方法
views::take
only yields a sized range 如果给定范围本身的大小。和 views::iota
isn't a sized range 除非您使用给它一个大小的二元素构造函数之一。你没有。
至于为什么 take_view
仅在底层迭代器被调整大小时才被调整,这是因为当达到要获取的元素数量时 take_view
停止或者基础范围的结束。这意味着尺寸可能小于您要求的尺寸。因此,要计算 take_view
的大小,您必须能够计算基础范围的大小,以查看它是否小于给定的计数。如果您碰巧传递了一个永远不需要计算大小的计数,这并不重要;它是一个编译时属性,而不是基于您在运行时碰巧赋予它的值。
Range V3 如何“工作”尚不清楚,但 C++20 标准不允许它工作。
,使用 range-v3 后编译。 C++23 需要此功能,还是 LWG 问题?
没有也没有。
take(0)
是一个奇怪的例子,因为它表明 0
可能很重要——如果我们可以根据值做出决定,那么 take(0)
总是会给你 { {1}} 表示右侧的 empty<T>
(即 T
)。
所以让我们考虑take(5)。
sized_range
为您提供最多 take(5)
个元素的范围。但是只有知道输入范围内有多少个元素才能知道有多少,而且只有输入范围是5
才能知道,这就是C++20的sized_range
的操作方式.但实际上还有另一种方法可以知道 take
有多少个元素,而 r | take(5)
不是 r
:如果我们知道 sized_range
是 无限 em> 范围。显然,从无限范围中取出 r
元素会给你一个包含 5
元素的范围(最后我检查过,无穷大实际上大于 5
,即使对于非常大的 { {1}})。
在 range-v3 中,5
是无限范围。我的意思是,它在 C++20 中也是无限范围,但 C++20 范围没有无限范围的概念,而 range-v3 does:
5
我们这里的情况满足 iota(0)
,因此我们将 template<typename From,typename To /* = unreachable_sentinel_t*/>
struct RANGES_EMPTY_BASES iota_view
: view_facade<iota_view<From,To>,same_as<To,unreachable_sentinel_t>
? infinite
: std::is_integral<From>::value && std::is_integral<To>::value
? finite
: unknown>
{
传递到 range-v3 所指的“基数”。
same_as<To,unreachable_sentinel_t>
然后检测到 its input is infinite 并返回 infinite
作为哨兵:
take
所以在这种情况下,我们的 default_sentinel
/CPP_auto_member
constexpr auto CPP_fun(end)()(const //
requires range<Rng const>)
{
if constexpr(sized_range<Rng const>)
if constexpr(random_access_range<Rng const>)
return ranges::begin(base_) +
static_cast<range_difference_t<Rng>>(size());
else
return default_sentinel;
// Not to spec: Infinite ranges:
else if constexpr(is_infinite<Rng const>::value)
return default_sentinel;
else
return sentinel<true>{ranges::end(base_)};
}
对是 iterator
/sentinel
,这对满足 counted_iterator
,所以 default_sentinel
有效.当 the count hits zero 和减法只是 negates the count 时,它们相等。
C++20 在设计上没有这种无限范围的概念。目前还不清楚将来是否会这样做。我什至不确定 Eric Niebler 和 Casey Carter 对 range-v3 中的设计是否满意。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。