【发布时间】:2021-02-02 13:56:42
【问题描述】:
我对 C++20 中的范围库有以下疑问:
让std::ranges::contiguous_range<T> 代表一个任意类型的T。
我可以假设std::ranges::sized_range<T>吗?
【问题讨论】:
标签: c++ stl iterator language-lawyer c++20
我对 C++20 中的范围库有以下疑问:
让std::ranges::contiguous_range<T> 代表一个任意类型的T。
我可以假设std::ranges::sized_range<T>吗?
【问题讨论】:
标签: c++ stl iterator language-lawyer c++20
不,不是每个contiguous_range 都是sized_range。
最简单的例子是一个以空字符结尾的字符串。它是连续的,但我们不知道它在O(1) 时间的大小。我们可以很容易地使用哨兵来表示这样的事情:
struct ntbs_sentinel {
bool operator==(char const* p) const {
return *p == '\0';
}
};
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)这里的结果范围仍然是连续的,但我们不知道它在哪里结束(在第一种情况下)或从哪里开始(在第二种情况下),所以我们不知道它的大小。
【讨论】:
noexcept,添加constexpr,然后使用强制转换为bool,而不是与一些文字进行比较。
value_type)转换为bool,并且该转换意味着结束列表已到达。考虑到这对于范围来说是多么罕见,最好不要太笼统,更明确地说明你在做什么。
value_type终止。
由于存在哨兵,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 models ranges::forward_range<T>(确实如此)和 sentinel_t<T> 和 iterator_t<T> models std::sized_sentinel_for<sentinel_t<T>, iterator_t<T>>。
如前所述,确实如此。
【讨论】:
没有。
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>>>;
};
如你所见,requiresrandom_access_range,即:
template<class T>
concept random_access_range =
ranges::bidirectional_range<T> && std::random_access_iterator<ranges::iterator_t<T>>;
另一方面,requiresbidirectional_range,即:
template<class T>
concept bidirectional_range =
ranges::forward_range<T> && std::bidirectional_iterator<ranges::iterator_t<T>>;
其中requiresforward_range,即:
template<class T>
concept forward_range =
range::input_range<T> && std::forward_iterator<ranges::iterator_t<T>>;
还有那个requiresinput_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_iterators 玩类似的游戏。 std::ranges::size 无处可寻(启用 sized_range)。
【讨论】: