contiguous_range 总是 sized_range 吗?
Is a contiguous_range always a sized_range?
关于 C++20 中的范围库,我有以下问题:
让 std::ranges::contiguous_range<T>
为 任意 键入 T。
我可以假设 std::ranges::sized_range<T>
吗?
不,不是每个 contiguous_range
都是 sized_range
。
最简单的示例是 null-terminated 字符串。它是连续的,但我们不知道它在 O(1)
时间内的大小。我们可以使用哨兵轻松地表示这样的事情:
struct ntbs_sentinel {
bool operator==(char const* p) const {
return *p == '[=10=]';
}
};
struct ntbs {
char const* p;
char const* begin() const { return p; }
ntbs_sentinel end() const { return {}; }
};
static_assert(std::ranges::contiguous_range<ntbs>);
static_assert(!std::ranges::sized_range<ntbs>);
另一个例子是,给定一些 std::string
对象 s
和一些谓词 p
,或者:
s | std::views::take_while(p)
s | std::views::drop_while(p)
此处的结果范围仍然是连续的,但我们不知道它在哪里结束(在第一种情况下)或从哪里开始(在第二种情况下),所以我们不知道它的大小是多少。
没有
contiguous_range
是:
template<class T>
concept contiguous_range =
ranges::random_access_range<T> &&
std::contiguous_iterator<ranges::iterator_t<T>> &&
requires(T& t) {
{ ranges::data(t) } ->
std::same_as<std::add_pointer_t<ranges::range_reference_t<T>>>;
};
而且,如您所见,它 requires
random_access_range
,即:
template<class T>
concept random_access_range =
ranges::bidirectional_range<T> && std::random_access_iterator<ranges::iterator_t<T>>;
另一方面,requires
bidirectional_range
,即:
template<class T>
concept bidirectional_range =
ranges::forward_range<T> && std::bidirectional_iterator<ranges::iterator_t<T>>;
其中requires
forward_range
,即:
template<class T>
concept forward_range =
range::input_range<T> && std::forward_iterator<ranges::iterator_t<T>>;
和那个requires
input_range
,所以它需要:
template<class T>
concept input_range =
ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;
而 range
只有 requires
std::ranges::begin()
和 std::ranges::end()
对给定的 T
.
有效
你可以和那些 std::XXX_iterator
玩类似的游戏。 std::ranges::size
无处可用(启用 sized_range
)。
由于哨兵的存在,contiguous_range<T>
不足以被视为 sized_range<T>
。但是,如果将 contiguous_range<T>
与 common_range<T>
结合使用(这要求哨兵是迭代器),则 sized_range<T>
也必须为真。
这是逻辑。 contiguous_range<T>
也是 random_access_range<T>
。 random_access_range<T>
部分意味着 random_access_iterator<iterator_t<T>>
is true. common_range<T>
means that is_same<iterator_t<T>, sentinel_t<T>>
。因此,random_access_iterator<sentinel_t<T>>
也必须为真。
现在,random_access_iterator<It>
imposes a requirement std::sized_sentinel_for<I, I>
是正确的。由于 iterator_t<T>
和 sentinel_t<T>
是同一类型,这意味着 std::sized_sentinel_for<sentinel_t<T>, iterator_t<T>>
也必须为真。
所以,让我们看看sized_range<T>
。这要求 std::ranges::size(t)
对 T
.
类型的 t
有效
ranges::size<T>
is valid if T
模型 ranges::forward_range<T>
(它确实如此)和 sentinel_t<T>
和 iterator_t<T>
模型 std::sized_sentinel_for<sentinel_t<T>, iterator_t<T>>
.
如前所述,确实如此。
关于 C++20 中的范围库,我有以下问题:
让 std::ranges::contiguous_range<T>
为 任意 键入 T。
我可以假设 std::ranges::sized_range<T>
吗?
不,不是每个 contiguous_range
都是 sized_range
。
最简单的示例是 null-terminated 字符串。它是连续的,但我们不知道它在 O(1)
时间内的大小。我们可以使用哨兵轻松地表示这样的事情:
struct ntbs_sentinel {
bool operator==(char const* p) const {
return *p == '[=10=]';
}
};
struct ntbs {
char const* p;
char const* begin() const { return p; }
ntbs_sentinel end() const { return {}; }
};
static_assert(std::ranges::contiguous_range<ntbs>);
static_assert(!std::ranges::sized_range<ntbs>);
另一个例子是,给定一些 std::string
对象 s
和一些谓词 p
,或者:
s | std::views::take_while(p)
s | std::views::drop_while(p)
此处的结果范围仍然是连续的,但我们不知道它在哪里结束(在第一种情况下)或从哪里开始(在第二种情况下),所以我们不知道它的大小是多少。
没有
contiguous_range
是:
template<class T>
concept contiguous_range =
ranges::random_access_range<T> &&
std::contiguous_iterator<ranges::iterator_t<T>> &&
requires(T& t) {
{ ranges::data(t) } ->
std::same_as<std::add_pointer_t<ranges::range_reference_t<T>>>;
};
而且,如您所见,它 requires
random_access_range
,即:
template<class T>
concept random_access_range =
ranges::bidirectional_range<T> && std::random_access_iterator<ranges::iterator_t<T>>;
另一方面,requires
bidirectional_range
,即:
template<class T>
concept bidirectional_range =
ranges::forward_range<T> && std::bidirectional_iterator<ranges::iterator_t<T>>;
其中requires
forward_range
,即:
template<class T>
concept forward_range =
range::input_range<T> && std::forward_iterator<ranges::iterator_t<T>>;
和那个requires
input_range
,所以它需要:
template<class T>
concept input_range =
ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;
而 range
只有 requires
std::ranges::begin()
和 std::ranges::end()
对给定的 T
.
你可以和那些 std::XXX_iterator
玩类似的游戏。 std::ranges::size
无处可用(启用 sized_range
)。
由于哨兵的存在,contiguous_range<T>
不足以被视为 sized_range<T>
。但是,如果将 contiguous_range<T>
与 common_range<T>
结合使用(这要求哨兵是迭代器),则 sized_range<T>
也必须为真。
这是逻辑。 contiguous_range<T>
也是 random_access_range<T>
。 random_access_range<T>
部分意味着 random_access_iterator<iterator_t<T>>
is true. common_range<T>
means that is_same<iterator_t<T>, sentinel_t<T>>
。因此,random_access_iterator<sentinel_t<T>>
也必须为真。
现在,random_access_iterator<It>
imposes a requirement std::sized_sentinel_for<I, I>
是正确的。由于 iterator_t<T>
和 sentinel_t<T>
是同一类型,这意味着 std::sized_sentinel_for<sentinel_t<T>, iterator_t<T>>
也必须为真。
所以,让我们看看sized_range<T>
。这要求 std::ranges::size(t)
对 T
.
t
有效
ranges::size<T>
is valid if T
模型 ranges::forward_range<T>
(它确实如此)和 sentinel_t<T>
和 iterator_t<T>
模型 std::sized_sentinel_for<sentinel_t<T>, iterator_t<T>>
.
如前所述,确实如此。