我对 C++20 中的范围库有以下问题:
让任意类型 Tstd::ranges::contiguous_range<T>
。
我可以假设std::ranges::sized_range<T>
吗?
不,不是每个contiguous_range
都是sized_range
。
最简单的示例是以 null 结尾的字符串。它是连续的,但我们不知道它的大小O(1)
时间。我们可以使用哨兵轻松表示这样的事情:
struct ntbs_sentinel {
bool operator==(char const* p) const {
return *p == ' ';
}
};
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<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>>
是真的。common_range<T>
意味着is_same<iterator_t<T>, sentinel_t<T>>
.因此,random_access_iterator<sentinel_t<T>>
也必须是正确的。
现在,random_access_iterator<It>
强加了一个要求,即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
有效。
如果T
模型ranges::forward_range<T>
(确实如此),并且sentinel_t<T>
和iterator_t<T>
模型std::sized_sentinel_for<sentinel_t<T>, iterator_t<T>>
,则ranges::size<T>
有效。
如前所述,确实如此。
No.
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
)。