在什么情况下 ref_view{E} 是病式而 subrange{E} 不是?

Under what circumstances is ref_view{E} ill-formed and subrange{E} not?

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

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

第一种情况代表一个view's type is not changed after being piped with 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}是病态的。

它的目的是什么?有人可以举个例子吗?

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} 的右值。就像一个子范围右值。可能是其他的,取决于子范围的推导指南。

But regarding the third case, I can't think of under what circumstances subrange{E} is well-formed and ref_view{E} is ill-formed.

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<int> 的东西,那么它不是视图(第一个项目符号),你也不能从它构造 ref_view(因为它是右值),你也不能从中构造一个 subrange(因为它是一个非借用范围)。


我意识到这并不能完全回答问题,因为我在输入答案时颠倒了我脑子里的极性。但是 .

另一个纯假设的例子是一个非视图范围,它将其内容存储在一个 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)-可破坏的),你不能 ref_view{E} 它(因为它是一个右值),但这样的范围仍然可以被借用,所以 subrange{E} 可以工作。

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

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