在什么情况下 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)
的表达式等效于(具有相同的效果):
decay-copy(E)
if E
模型的衰减类型 view
.
- 否则,
ref_view{E}
如果该表达式格式正确
- 否则,
subrange{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_view
和 span
等类型是借用的。
所以如果你有类似右值 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_range
和 view
都是可选的)。
C++20 引入了 views::all
,它是一个范围适配器,returns 一个 view
包含其范围参数的所有元素。
表达式 views::all(E)
的表达式等效于(具有相同的效果):
decay-copy(E)
ifE
模型的衰减类型view
.- 否则,
ref_view{E}
如果该表达式格式正确 - 否则,
subrange{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 andref_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_view
和 span
等类型是借用的。
所以如果你有类似右值 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_range
和 view
都是可选的)。