简体   繁体   English

为什么每个循环的 c++ 接受右值但 std::ranges 不接受?

[英]Why does c++ for each loops accept r-values but std::ranges do not?

A statement like this compiles without error:像这样的语句编译没有错误:

for (int i : std::vector<int>({0, 1}))
  std::cout << i;

But a statement like this does not:但是这样的声明不会:

std::vector<int>({0, 1}) |
  std::views::filter([](int i) { return true; });

Why are r-values allowed in for each loops but not in std::range pipes?为什么每个循环都允许 r 值,但 std::range 管道中不允许? Is there a way I could get something like the second to work, without having to declare a variable?有没有办法让我可以像第二个一样工作,而不必声明变量?

The for-loop works fine because the vector<int> (rvalue) is valid while the loop is evaluated. for 循环工作正常,因为vector<int> (右值)在评估循环时有效。

The second code snippet has 2 issues:第二个代码片段有两个问题:

  • The predicate must return a bool谓词必须返回一个bool
  • the vector<int> you are using in the views object is dangling by the time it is evaluated, so the compiler does not accept it.您在视图 object 中使用的vector<int>在评估时悬空,因此编译器不接受它。

If instead of a vector<int> rvalue, the object cannot dangle (lvalue) or holds references into something else that cannot not dangle, this would work.如果不是vector<int>右值,object 不能悬挂(左值)或将引用保存到不能悬挂的其他东西中,这将起作用。

For example:例如:

auto vec = std::vector<int>({0, 1});
auto vw = vec | std::ranges::views::filter([](int i) { std::cout << i; return true; });

Or you can pass an rvalue that has references to a non-dangling vector<int> object, like std::span :或者你可以传递一个右值,它引用了一个非悬挂vector<int> object,比如std::span

auto vec = std::vector<int>({0, 1});
auto vw = span{vec} | std::ranges::views::filter([](int i) { std::cout << i; return true; })

Here, std::span is still a rvalue, but the compiler accepts it because the author of std::span has created an exception for it.这里, std::span仍然是一个右值,但是编译器接受它,因为std::span的作者已经为它创建了一个异常。

For a custom type with references into something else (typically views), you can create your own exception by creating a specialization of the template variable enable_borrowed_range .对于引用其他内容(通常是视图)的自定义类型,您可以通过创建模板变量enable_borrowed_range的特化来创建自己的异常。

In the case of vector it would look like this (but don't do this! ):在矢量的情况下,它看起来像这样(但不要这样做! ):

template<> // don't do this
inline constexpr bool ranges::enable_borrowed_range<std::vector<int>> = true;

Your code now compiles but it will trigger undefined behavior because the vector is dangling once the view is evaluated.您的代码现在可以编译,但它会触发未定义的行为,因为一旦评估视图,向量就会悬空。

This works with latest MSVC STL and libstdc++ ( demo ), thanks to P2415R2 .这适用于最新的 MSVC STL 和 libstdc++(演示),感谢P2415R2

Prior to P2415R2, filter_view always stores the underlying vector by reference (via a ref_view ).在 P2415R2 之前, filter_view始终通过引用(通过ref_view )存储基础vector It was deemed unwise to store a copy of vector .存储vector的副本被认为是不明智的。 Because a view is supposed to be cheap to copy, cheap to move, and cheap to destroy.因为视图应该是廉价复制、廉价移动和廉价销毁。 Storing a copy of vector would have unexpected performance penalty.存储vector的副本会带来意想不到的性能损失。

And due to lifetime issues, it doesn't make sense to have a reference to rvalue vector .由于生命周期问题,引用右值vector没有意义。

But P2415R2 points out that a view can as well be non-copyable, while still be cheap to move and reasonably cheap to destroy.但 P2415R2 指出,视图也可以是不可复制的,同时移动和销毁的成本仍然很低。

So P2415R2 introduced owning_view , a non-copyable view that stores a copy of a range rvalue, and made standard range adaptors use that view.因此 P2415R2 引入了owning_view ,一个存储范围右值副本的不可复制视图,并使标准范围适配器使用该视图。

The end result is, creating a filter_view from a vector rvalue works with standard library implementations that implement P2415R2.最终结果是,从vector右值创建filter_view与实现 P2415R2 的标准库实现一起工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM