[英]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
.还有其他几个熟悉的借用范围——比如span
和string_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 andenable_borrowed_range<remove_cv_t<T>>
isfalse
,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
的特化): subrange
、 span
、 string_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.