为什么`iota(0) | take(0)` 不是 C++20 中的模型 ranges::sized_range?
Why does `iota(0) | take(0)` not model ranges::sized_range in C++20?
考虑以下代码片段:
#include <ranges>
auto r = std::views::iota(0) | std::views::take(0);
static_assert(std::ranges::sized_range<decltype(r)>);
gcc-trunk rejects it for required-expression std::ranges::size(r)
is invalid. Why does r
not model 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)>);
问题似乎是 range-v3
中 r
的 sentinel_t
只是 ranges::default_sentinel
满足这两种类型的 std::sized_sentinel_for<ranges::counted_iterator>
, since there is a valid operator-
in [predef.iterators#iterators.counted]:
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);
但在namepace std::ranges
中,r
的sentinel_t
是std::ranges::take_view<std::ranges::iota_view>::_Sentinel<true>
,无法转换为std::default_sentinel_t
。
views::take
only yields a sized range if the given range itself was sized. And views::iota
isn't a sized range 除非您使用赋予它大小的二元素构造函数之一。你没有。
至于为什么take_view
只在底层迭代器定大小时才定大,这是因为take_view
在达到取元素个数时停止或 您到达基础范围的末端。这意味着尺寸可能小于您要求的尺寸。因此,要计算 take_view
的大小,您必须能够计算基础范围的大小,以查看它是否小于给定的计数。如果您碰巧传递了一个永远不需要计算大小的计数,那也没关系;它是编译时属性,而不是基于你在运行时碰巧给它的值。
Range V3 的“工作原理”尚不清楚,但 C++20 标准不允许它工作。
Compiles after using range-v3. Is this feature required for C++23, or is it an LWG issue?
没有也没有。
take(0)
是一个奇怪的例子,因为它表明 0
可能很重要——如果我们可以根据值做出决定,那么 take(0)
总是会给出你empty<T>
为右T
(这是一个sized_range
)。
所以让我们考虑一下 take(5)。
take(5)
给你一个范围,其中最多 5
个元素。但是我们只有知道输入范围内有多少个元素才能知道有多少,只有输入范围是sized_range
才能知道,这就是C++20的take
的操作方式.但实际上还有另一种方法可以知道 r | take(5)
有多少元素而不 r
是 sized_range
:如果我们知道 r
是 无限 范围。显然,将 5
个元素从无限范围中取出会给你一个带有 5
个元素的范围(上次我检查过,无穷大实际上大于 5
,即使对于非常大的 5
).
在range-v3中,iota(0)
是无限范围。我的意思是,它在 C++20 中也是一个无限范围,但是 C++20 范围没有这种无限范围的概念,而 range-v3 does:
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>
{
我们这里的案例满足 same_as<To, unreachable_sentinel_t>
,所以我们将 infinite
传递到 range-v3 所指的“基数”。
take
然后检测到 its input is infinite 和 returns 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
,这个对满足sized_sentinel_for
,所以ranges::size
作品。当 the count hits zero and subtraction simply negates the count.
时它们是相等的
C++20 简单地说,在设计上没有这种无限范围的概念。目前还不清楚未来是否会。我什至不确定 Eric Niebler 和 Casey Carter 是否对 range-v3 中的设计感到满意。
考虑以下代码片段:
#include <ranges>
auto r = std::views::iota(0) | std::views::take(0);
static_assert(std::ranges::sized_range<decltype(r)>);
gcc-trunk rejects it for required-expression std::ranges::size(r)
is invalid. Why does r
not model 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)>);
问题似乎是 range-v3
中 r
的 sentinel_t
只是 ranges::default_sentinel
满足这两种类型的 std::sized_sentinel_for<ranges::counted_iterator>
, since there is a valid operator-
in [predef.iterators#iterators.counted]:
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);
但在namepace std::ranges
中,r
的sentinel_t
是std::ranges::take_view<std::ranges::iota_view>::_Sentinel<true>
,无法转换为std::default_sentinel_t
。
views::take
only yields a sized range if the given range itself was sized. And views::iota
isn't a sized range 除非您使用赋予它大小的二元素构造函数之一。你没有。
至于为什么take_view
只在底层迭代器定大小时才定大,这是因为take_view
在达到取元素个数时停止或 您到达基础范围的末端。这意味着尺寸可能小于您要求的尺寸。因此,要计算 take_view
的大小,您必须能够计算基础范围的大小,以查看它是否小于给定的计数。如果您碰巧传递了一个永远不需要计算大小的计数,那也没关系;它是编译时属性,而不是基于你在运行时碰巧给它的值。
Range V3 的“工作原理”尚不清楚,但 C++20 标准不允许它工作。
Compiles after using range-v3. Is this feature required for C++23, or is it an LWG issue?
没有也没有。
take(0)
是一个奇怪的例子,因为它表明 0
可能很重要——如果我们可以根据值做出决定,那么 take(0)
总是会给出你empty<T>
为右T
(这是一个sized_range
)。
所以让我们考虑一下 take(5)。
take(5)
给你一个范围,其中最多 5
个元素。但是我们只有知道输入范围内有多少个元素才能知道有多少,只有输入范围是sized_range
才能知道,这就是C++20的take
的操作方式.但实际上还有另一种方法可以知道 r | take(5)
有多少元素而不 r
是 sized_range
:如果我们知道 r
是 无限 范围。显然,将 5
个元素从无限范围中取出会给你一个带有 5
个元素的范围(上次我检查过,无穷大实际上大于 5
,即使对于非常大的 5
).
在range-v3中,iota(0)
是无限范围。我的意思是,它在 C++20 中也是一个无限范围,但是 C++20 范围没有这种无限范围的概念,而 range-v3 does:
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>
{
我们这里的案例满足 same_as<To, unreachable_sentinel_t>
,所以我们将 infinite
传递到 range-v3 所指的“基数”。
take
然后检测到 its input is infinite 和 returns 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
,这个对满足sized_sentinel_for
,所以ranges::size
作品。当 the count hits zero and subtraction simply negates the count.
C++20 简单地说,在设计上没有这种无限范围的概念。目前还不清楚未来是否会。我什至不确定 Eric Niebler 和 Casey Carter 是否对 range-v3 中的设计感到满意。