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>>;

另一方面,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_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>>.

如前所述,确实如此。