简体   繁体   English

视图迭代器在视图的生命周期之后是否有效?

[英]Are view iterators valid beyond the lifetime of the view?

Say I have a custom container class that stores data in a map:假设我有一个自定义容器 class 将数据存储在 map 中:

class Container
{
  public:
    void add(int key, std::string value) { _data.emplace(key, std::move(value)); }

  private:
    std::map<int, std::string> _data;
};

I want to provide an interface to access the values (not the keys) of the map.我想提供一个接口来访问 map 的值(不是键)。 The ranges library provides std::views::values to give me a range of the map's values:范围库提供std::views::values给我一个地图值的范围:

auto values() { return std::views::values(_data); }

Usage:用法:

Container c;
c.add(1, "a");
c.add(3, "b");
c.add(2, "c");

for (auto &value : c.values())
    std::cout << value << " ";  // Prints "a c b"

But since I want to treat my class as a container, I want to have begin() and end() iterators.但是因为我想把我的 class 当作一个容器,我想要begin()end()迭代器。 Can I do this?我可以这样做吗?

auto begin() { return std::ranges::begin(values()); }
auto end() { return std::ranges::end(values()); }

Here I'm calling values() to get the range to the map's values, and getting an iterator to the beginning of the range (or the sentinel end iterator).在这里,我调用values()来获取地图值的范围,并将迭代器获取到范围的开头(或哨兵结束迭代器)。 But the range itself goes out of scope and is destroyed.但范围本身超出 scope 并被破坏。 Is the iterator itself still valid?迭代器本身是否仍然有效?

From this example , it seems like the iterator is valid.这个例子中,似乎迭代器是有效的。 But is that guaranteed by the standard, either for std::views:values specifically or for views in general?但是标准是否保证了这一点,无论是针对std::views:values还是针对一般视图?

Are view iterators valid beyond the lifetime of the view?视图迭代器在视图的生命周期之后是否有效?

The property here is called a borrowed range.这里的属性称为借用范围。 If a range is a borrowed range, then its iterators are still valid even if a range is destroyed.如果一个范围是一个借用范围,那么即使一个范围被破坏,它的迭代器仍然有效。 R& , if R is a range, is the most trivial kind of borrowed range - since it's not the lifetime of the reference that the iterators would be tied into. R& ,如果R是一个范围,是最简单的借用范围 - 因为它不是迭代器将被绑定到的引用的生命周期。 There are several other familiar borrowed ranges - like span and string_view .还有其他几个熟悉的借用范围——比如spanstring_view

Some range adaptors are conditionally borrowed ( P2017 ).有条件借用了一些范围适配器( P2017 )。 That is, they don't add any additional state on top of the range they are adapting -- so the adapted range can be borrowed if the underlying range is (or underlying ranges are).也就是说,他们不会在他们正在适应的范围之上添加任何额外的 state - 因此,如果基础范围是(或基础范围是),则可以借用适应的范围。 For instance, views::reverse(r) is borrowed whenever r is borrowed.例如,只要借用r ,就会借用views::reverse(r) But views::split(r, pat) isn't conditionally borrowed - because the pattern is stored in the adaptor itself rather than in the iterators (hypothetically, it could also be stored in the iterators, at a cost).但是views::split(r, pat)不是有条件地借用的——因为模式存储在适配器本身而不是迭代器中(假设它也可以存储在迭代器中,但要付出代价)。

views::values(r) is an example of such: it is a borrowed range whenver r is borrowed. views::values(r)就是一个例子:当r被借用时,它就是一个借用范围。 And, in your example, the underlying range is a ref_view , which is itself always borrowed (by the same principle that R& is always borrowed).而且,在您的示例中,基础范围是ref_view ,它本身总是被借来的(与R&总是被借用的原理相同)。

Note that here:注意这里:

auto begin() { return std::ranges::begin(values()); }
auto end() { return std::ranges::end(values()); }

Passing an rvalue range into ranges::begin is only valid if the range is borrowed.将右值范围传递给ranges::begin仅在借用范围时才有效。 That's [range.access.begin]/2.1 :那是[range.access.begin]/2.1

If E is an rvalue and enable_borrowed_range<remove_cv_t<T>> is false , ranges::begin(E) is ill-formed.如果E是右值并且enable_borrowed_range<remove_cv_t<T>>false ,则ranges::begin(E)格式错误。

So because your original code compiled, you can be sure that it is valid.所以因为你的原始代码编译过,你可以确定它是有效的。

The C++20 standard recognizes the concept of a range type whose iterators can outlive a particular object of that range type. C++20 标准承认范围类型的概念,其迭代器可以比该范围类型的特定 object 寿命更长。 This is called a "borrowed range".这称为“借用范围”。 The standard defines several types which are borrowed ranges (by providing specializations of ranges::enable_borrowed_range ): subrange , span , string_view , and a couple of others.该标准定义了几种借用范围的类型(通过提供ranges::enable_borrowed_range的特化): subrangespanstring_view和其他几个。

However, no other standard library types have specializations of that variable.但是,没有其他标准库类型具有该变量的特化。 So those types are not borrowed ranges.所以这些类型不是借来的范围。

std::views::elements_view (which values_view is an alias of) is not a borrowed range. std::views::elements_view (其中values_view是其别名)不是借用范围。 Therefore, you cannot assume that iterators which outlive the range are still valid.因此,您不能假设超出范围的迭代器仍然有效。

And even if it were a borrowed range, it is never valid to compare iterators/sentinels from different ranges (even different instances of the same view of the same range).即使它是一个借用的范围,比较来自不同范围的迭代器/哨兵(甚至是同一范围的同一视图的不同实例)也永远无效。

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

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