为什么在最新的编译器中允许将不可视范围的rvalue范围传递给范围适配器?

huangapple go评论73阅读模式
英文:

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&lt;class T&gt;
  concept viewable_range =
    range&lt;T&gt; &amp;&amp;
    ((view&lt;remove_cvref_t&lt;T&gt;&gt; &amp;&amp; constructible_from&lt;remove_cvref_t&lt;T&gt;, T&gt;) ||
     (!view&lt;remove_cvref_t&lt;T&gt;&gt; &amp;&amp;
      (is_lvalue_reference_v&lt;T&gt; || (movable&lt;remove_reference_t&lt;T&gt;&gt; &amp;&amp; !is-initializer-list&lt;T&gt;))));

Note the last part:

(!view&lt;remove_cvref_t&lt;T&gt;&gt; &amp;&amp;
  (is_lvalue_reference_v&lt;T&gt; || (movable&lt;remove_reference_t&lt;T&gt;&gt; &amp;&amp; !is-initializer-list&lt;T&gt;))));

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.

huangapple
  • 本文由 发表于 2023年7月17日 10:42:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76701219.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定