繁体   English   中英

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

[英]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是一个范围,是最简单的借用范围 - 因为它不是迭代器将被绑定到的引用的生命周期。 还有其他几个熟悉的借用范围——比如spanstring_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的特化): subrangespanstring_view和其他几个。

但是,没有其他标准库类型具有该变量的特化。 所以这些类型不是借来的范围。

std::views::elements_view (其中values_view是其别名)不是借用范围。 因此,您不能假设超出范围的迭代器仍然有效。

即使它是一个借用的范围,比较来自不同范围的迭代器/哨兵(甚至是同一范围的同一视图的不同实例)也永远无效。

暂无
暂无

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

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