英文:
Why is passing rvalue range that is not viewable range to range adaptor allowed in latest compiler?
问题
我正在学习关于范围的知识,了解到关于范围适配器的时候,根据 cppreference RangeAdaptorObject,它们只接受 viewable_range
作为第一个参数。
但是当我在 Compiler Explorer 上测试了以下代码:
auto val = std::views::drop(std::vector{1, 2, 3, 4, 5}, 2);
我发现对于 clang 16.0.0 和 gcc 12.0(都使用 -std=c++20
),这段代码可以编译,而对于任何早期版本都无法编译。
我还尝试了:
auto val = std::views::transform(std::vector{1, 2, 3, 4, 5}, 2);
这在所有版本的编译器中都无法编译。
那么为什么在较新版本的编译器中允许将非 viewable_range
传递给某些范围适配器呢?
更新:
感谢 @康桓瑋 指出了我的笔误,views::transform
在可移动的范围上是可以编译的:
auto val = std::views::transform(std::vector{1, 2, 3, 4, 5}, [](int i) { return i; });
英文:
I am learning about ranges and as I learned about range adaptors, they only accept viewable_range
as the first argument according to cppreference RangeAdaptorObject.
But when I tested this code on Compiler Explorer:
auto val = std::views::drop(std::vector{1, 2, 3, 4, 5}, 2);
I found that for clang 16.0.0 and gcc 12.0 (both with -std=c++20
), this code does compile, while for any older version it does not.
I also tried:
auto val = std::views::transform(std::vector{1, 2, 3, 4, 5}, 2);
This does not compile for any versions of compilers.
So why is it allowed to passing non-viewable_range to some range adaptors in newer version of compiler?
update:
Thanks for @康桓瑋 to point out my typo, views::transform
does compile with rvalue movable range:
auto val = std::views::transform(std::vector{1, 2, 3, 4, 5}, [](int i) { return i; });
答案1
得分: 4
C++20 DR(缺陷报告)P2415 增强了viewable_range
的定义:
template<class T>
concept viewable_range =
range<T> &&
((view<remove_cvref_t<T>> && constructible_from<remove_cvref_t<T>, T>) ||
(!view<remove_cvref_t<T>> &&
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
请注意最后一部分:
(!view<remove_cvref_t<T>> &&
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
我们不再要求非view
范围必须是左值引用,为此引入了新的仅支持移动的 owning_view
来处理这种情况。
owning_view
将将右值非view
范围移动到其内部成员以拥有数据,并且只通过移动操作传递所有权,仍然满足由该文档更新的 view
的语义要求。
因此,在 C++20 中,右值 vector
也是一个 viewable_range
,因为它可以转换为 owning_view
。
举个例子,views::drop(std::vector{1, 2, 3, 4, 5}, 2)
首先将右值 vector
移动到 owning_view
,然后将 owning_view
移动到 drop_view
。由于 drop_view
重新获得了数据的所有权,因此它也是仅支持移动的。
英文:
C++20 DR (Defect Report) P2415 enhances the definition of viewable_range
to:
template<class T>
concept viewable_range =
range<T> &&
((view<remove_cvref_t<T>> && constructible_from<remove_cvref_t<T>, T>) ||
(!view<remove_cvref_t<T>> &&
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
Note the last part:
(!view<remove_cvref_t<T>> &&
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
We no longer require that the non-view
range must be an lvalue reference, in which case a new move-only owning_view
was introduced to handle such cases.
owning_view
will move the rvalue non-view
range to its internal member to take ownership of the data, and make the transfer of ownership only through the move operation, which still meets the semantic requirements of the view
updated by the paper.
So in C++20, an rvalue vector
is also a viewable_range
because it can be converted to owning_view
.
As your example, views::drop(std::vector{1, 2, 3, 4, 5}, 2)
will first move the rvalue vector
into owning_view
, then moves owning_view
into drop_view
. Since the drop_view
regains ownership of the data, it is also move-only.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论