【问题标题】:Under what circumstances is ref_view{E} ill-formed and subrange{E} not?在什么情况下 ref_view{E} 格式错误而 subrange{E} 不是?
【发布时间】:2021-07-31 15:17:29
【问题描述】:

C++20 引入了views::all,它是一个范围适配器,它返回一个包含其范围参数的所有元素的view

表达式views::all(E) 与以下表达式等效(具有相同的效果):

  • decay-copy(E) 如果E 的衰减类型模型view
  • 否则,ref_view{E} 如果该表达式格式正确
  • 否则,subrange{E}

第一种情况表示view 的类型在使用views::all (goldbot) 进行管道传输后没有改变:

auto r = views::iota(0);
static_assert(std::same_as<decltype(r), decltype(r | views::all)>);

第二种情况是用ref_view包裹一个viewable_range,方便范围管道操作:

int r[] = {0, 1, 2};
static_assert(std::same_as<ranges::ref_view<int[3]>, decltype(r | views::all)>);

但是关于第三种情况,我想不出在什么情况下subrange{E} 是良构的,ref_view{E} 是良构的。

它的目的是什么?谁能举个例子?

【问题讨论】:

    标签: c++ c++20 range-v3 std-ranges


    【解决方案1】:

    但是关于第三种情况,我想不出在什么情况下subrange{E}是良构的,ref_view{E}是良构的。

    ref_view{E} 仅适用于左值范围。

    subrange{E} 仅适用于借用范围。你可以在[range.subrange.general]找到它的推演指南:

    template<borrowed_­range R>
      subrange(R&&) ->
        subrange<iterator_t<R>, sentinel_t<R>,
                 (sized_­range<R> || sized_­sentinel_­for<sentinel_t<R>, iterator_t<R>>)
                   ? subrange_kind::sized : subrange_kind::unsized>;
    

    借用的范围要么是左值,要么是选择从中借用的范围。例如,string_viewspan 这样的类型是借用的。

    因此,如果您有类似右值 vector&lt;int&gt; 的东西,那么这不是视图(第一个项目符号),也不能从中构造 ref_view(因为它是右值),也不能从中构造 subrange它(因为它是非借用范围)。


    我意识到这并不能完全回答问题,因为我在输入答案时翻转了脑海中的极性。但是T.C.'s got me covered

    另一个纯粹假设的例子是一个非视图范围,它将其内容存储在shared_ptr 中,然后它的迭代器也共享该数据。比如:

    struct SharedVector {
        std::shared_ptr<std::vector<int>> data;
    
        struct Iterator {
            std::shared_ptr<std::vector<int>> data;
            std::vector<int>::iterator cur;
    
            // ...
        };
    
        auto begin() -> Iterator { return {data, data->begin()}; }
        auto end() -> Iterator { return {data, data->end()}; }
    };
    

    右值SharedVector 不是视图(不是O(1)-destructible),你不能ref_view{E} 它(因为它是一个右值),但是这样的范围仍然可以借用,所以subrange{E} 可以工作。

    【讨论】:

    • 为什么视图需要 O(1)-destructible?
    • @xskxzr 这是一个很好的问题involved answer
    • 其实是cplusplus.github.io/LWG/issue3452。该问题没有说明为什么视图应该是 O(1) 可破坏的。也许原因与 P2415 中提到的相同(使按值传递便宜)但这并不能说服我——“便宜”并不意味着“O(1)”。
    【解决方案2】:

    借用右值非视图范围(视图将属于第一个项目符号,因此不会出现问题)。

    当前标准中的典型示例是非零静态范围的span(如span&lt;int, 42&gt;)。另一个示例是不支持赋值的假设借用范围类型。也可以构建综合示例(因为 borrowed_rangeview 都是可选的)。

    【讨论】:

    • 注意P2325 std::span 变成了一个视图。
    • 不支持赋值的借用范围类型有什么实际用途吗?
    【解决方案3】:
    template<__NotSameAs<ref_view> T>
    requires std::convertible_to<T, R&> && requires { _FUN(std::declval<T>()); }
    constexpr ref_view(T&& t);
    

    由此看来,右值似乎是不确定的。

    template<class R>
    ref_view(R&) -> ref_view<R>;
    

    看起来它也阻止了右值。

    创建一个您可以subrange{E} 的右值。就像一个子范围右值。可能是其他的,取决于子范围的推导指南。

    【讨论】:

    • 我认为在这种情况下,E 是一个viewable_range,它是一个右值而不是一个view,但我找不到这样的E 使得返回类型为E | std::views::all subrange.
    • 我认为E 可以是std::span&lt;int, 3&gt;{v}godbolt.org/z/v6o84q111。但这可以通过 Barry 的P2325R0 来改善。
    猜你喜欢
    • 2023-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    相关资源
    最近更新 更多