![](/img/trans.png)
[英]Is there an std view for random access iterators, as std::span is for contiguous iterators?
[英]Are view iterators valid beyond the lifetime of the view?
假设我有一个自定义容器 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;
};
我想提供一个接口来访问 map 的值(不是键)。 范围库提供std::views::values
给我一个地图值的范围:
auto values() { return std::views::values(_data); }
用法:
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"
但是因为我想把我的 class 当作一个容器,我想要begin()
和end()
迭代器。 我可以这样做吗?
auto begin() { return std::ranges::begin(values()); }
auto end() { return std::ranges::end(values()); }
在这里,我调用values()
来获取地图值的范围,并将迭代器获取到范围的开头(或哨兵结束迭代器)。 但范围本身超出 scope 并被破坏。 迭代器本身是否仍然有效?
从这个例子中,似乎迭代器是有效的。 但是标准是否保证了这一点,无论是针对std::views:values
还是针对一般视图?
视图迭代器在视图的生命周期之后是否有效?
这里的属性称为借用范围。 如果一个范围是一个借用范围,那么即使一个范围被破坏,它的迭代器仍然有效。 R&
,如果R
是一个范围,是最简单的借用范围 - 因为它不是迭代器将被绑定到的引用的生命周期。 还有其他几个熟悉的借用范围——比如span
和string_view
。
有条件借用了一些范围适配器( P2017 )。 也就是说,他们不会在他们正在适应的范围之上添加任何额外的 state - 因此,如果基础范围是(或基础范围是),则可以借用适应的范围。 例如,只要借用r
,就会借用views::reverse(r)
。 但是views::split(r, pat)
不是有条件地借用的——因为模式存储在适配器本身而不是迭代器中(假设它也可以存储在迭代器中,但要付出代价)。
views::values(r)
就是一个例子:当r
被借用时,它就是一个借用范围。 而且,在您的示例中,基础范围是ref_view
,它本身总是被借来的(与R&
总是被借用的原理相同)。
注意这里:
auto begin() { return std::ranges::begin(values()); }
auto end() { return std::ranges::end(values()); }
将右值范围传递给ranges::begin
仅在借用范围时才有效。 那是[range.access.begin]/2.1 :
如果
E
是右值并且enable_borrowed_range<remove_cv_t<T>>
为false
,则ranges::begin(E)
格式错误。
所以因为你的原始代码编译过,你可以确定它是有效的。
C++20 标准承认范围类型的概念,其迭代器可以比该范围类型的特定 object 寿命更长。 这称为“借用范围”。 该标准定义了几种借用范围的类型(通过提供ranges::enable_borrowed_range
的特化): subrange
、 span
、 string_view
和其他几个。
但是,没有其他标准库类型具有该变量的特化。 所以这些类型不是借来的范围。
std::views::elements_view
(其中values_view
是其别名)不是借用范围。 因此,您不能假设超出范围的迭代器仍然有效。
即使它是一个借用的范围,比较来自不同范围的迭代器/哨兵(甚至是同一范围的同一视图的不同实例)也永远无效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.