contiguous_range 总是一个 sized_range 吗?

ple*_*ndo 22 c++ iterator stl language-lawyer c++20

我有以下关于 C++20 中的 range 库的问题:

std::ranges::contiguous_range<T>一个任意类型T.

我可以假设std::ranges::sized_range<T>吗?

Bar*_*rry 24

不,并非每个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>);
Run Code Online (Sandbox Code Playgroud)

另一个例子是,给定一些std::string对象s和一些谓词p,要么:

  • s | std::views::take_while(p)
  • s | std::views::drop_while(p)

这里的结果范围仍然是连续的,但我们不知道它在哪里结束(在第一种情况下)或从哪里开始(在第二种情况下)所以我们不知道它的大小是多少。


Nic*_*las 11

由于哨兵的存在,作为 acontiguous_range<T>不足以被视为 a sized_range<T>。然而,如果你结合contiguous_range<T>with common_range<T>(这要求哨兵是一个迭代器),那么sized_range<T>也必须为真。

这是逻辑。Acontiguous_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)对 at类型有效T

ranges::size<T>如果T模型ranges::forward_range<T>(它确实如此)和sentinel_t<T>iterator_t<T>模型是有效的std::sized_sentinel_for<sentinel_t<T>, iterator_t<T>>

如前所述,它确实如此。


Fur*_*ish 7

不。

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>>>;
  };
Run Code Online (Sandbox Code Playgroud)

并且,如您所见,它requires random_access_range是:

template<class T>
concept random_access_range =
  ranges::bidirectional_range<T> && std::random_access_iterator<ranges::iterator_t<T>>;
Run Code Online (Sandbox Code Playgroud)

另一方面,requires bidirectional_range,即:

template<class T>
concept bidirectional_range =
  ranges::forward_range<T> && std::bidirectional_iterator<ranges::iterator_t<T>>;
Run Code Online (Sandbox Code Playgroud)

其中requires forward_range,即:

template<class T>
concept forward_range =
  range::input_range<T> && std::forward_iterator<ranges::iterator_t<T>>;
Run Code Online (Sandbox Code Playgroud)

requires input_range,所以它需要:

template<class T>
concept input_range =
  ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;
Run Code Online (Sandbox Code Playgroud)

range只有requires那个std::ranges::begin()并且std::ranges::end()对 given 有效T


你可以用那些std::XXX_iterators玩类似的游戏。没有任何东西可以用于std::ranges::size(启用sized_range)。