【问题标题】:Is a contiguous_range always a sized_range?contiguous_range 总是一个sized_range 吗?
【发布时间】: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


    【解决方案1】:

    不,不是每个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,而不是与一些文字进行比较。
    • @Deduplicator:只有当迭代器(或迭代器的value_type)转换为bool并且该转换意味着结束列表已到达。考虑到这对于范围来说是多么罕见,最好不要太笼统,更明确地说明你在做什么。
    • @NicolBolas 至少一个 null_terminated_sequence-sentinel 很容易。
    • @Deduplicator:可能不是“null”专门终止,而是由默认构造的value_type终止。
    • @NicolBolas 所以,两个默认哨兵:一个停在一个错误的元素上,如果它不能编译一个默认构造的元素。还有一个用于特定于它的迭代器的行为,因此我们不需要像流迭代器中那样的自定义和潜在动态的。
    【解决方案2】:

    由于存在哨兵,contiguous_range&lt;T&gt; 不足以被视为sized_range&lt;T&gt;。但是,如果将contiguous_range&lt;T&gt;common_range&lt;T&gt; 结合使用(这要求哨兵是迭代器),那么sized_range&lt;T&gt; 也必须为真。

    这是逻辑。 contiguous_range&lt;T&gt; 也是 random_access_range&lt;T&gt;random_access_range&lt;T&gt; 部分表示 random_access_iterator&lt;iterator_t&lt;T&gt;&gt; is true. common_range&lt;T&gt; means that is_same&lt;iterator_t&lt;T&gt;, sentinel_t&lt;T&gt;&gt;。因此,random_access_iterator&lt;sentinel_t&lt;T&gt;&gt; 也必须为真。

    现在,random_access_iterator&lt;It&gt; imposes a requirement std::sized_sentinel_for&lt;I, I&gt; 是真的。由于iterator_t&lt;T&gt;sentinel_t&lt;T&gt; 是同一类型,这意味着std::sized_sentinel_for&lt;sentinel_t&lt;T&gt;, iterator_t&lt;T&gt;&gt; 也必须为真。

    那么,让我们看看sized_range&lt;T&gt;。这要求std::ranges::size(t)T 类型的t 有效。

    ranges::size&lt;T&gt; is valid if T models ranges::forward_range&lt;T&gt;(确实如此)和 sentinel_t&lt;T&gt;iterator_t&lt;T&gt; models std::sized_sentinel_for&lt;sentinel_t&lt;T&gt;, iterator_t&lt;T&gt;&gt;

    如前所述,确实如此。

    【讨论】:

      【解决方案3】:

      没有。

      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)。

      【讨论】:

      • FWIW,关于你的最后一句话:你可以拥有一个范围形式的无限数据流,但我很确定你不能拥有一个无限形式的数据流 连续范围。 :)
      • @Quuxplusone 正确。经过一番思考,我编辑了答案 - 最后一段并没有真正添加任何内容,并且可能导致潜在的混淆,例如您指出的那个。
      • @Quuxplusone:就迭代器模型而言,您可以拥有连续的“无限数据流”。在这里,“无限”仅仅意味着哨兵永远不等于任何迭代器。您最终可能会由于缺少可寻址内存而崩溃,但从概念范围的角度来看,它在逻辑上是有效的。
      猜你喜欢
      • 2014-09-25
      • 2017-01-08
      • 2012-11-16
      • 1970-01-01
      • 1970-01-01
      • 2011-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多