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

Are view iterators valid beyond the lifetime of the view?

假设我有一个自定义容器 class 在地图中存储数据:

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

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

我想提供一个接口来访问地图的值(不是键)。范围库提供 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() 来获取映射值的范围,并获取指向范围开头的迭代器(或哨兵结束迭代器)。但是范围本身超出范围并被破坏。迭代器本身是否仍然有效?

this example开始,迭代器似乎是有效的。但这是否由标准保证,无论是针对 std::views:values 还是针对一般视图?

Are view iterators valid beyond the lifetime of the view?

这里的属性称为借用范围。如果范围是借用范围,那么即使范围被销毁,它的迭代器仍然有效。 R&,如果 R 是一个范围,是最普通的借用范围 - 因为它不是迭代器将绑定到的 reference 的生命周期.还有其他几个熟悉的借用范围 - 如 spanstring_view.

一些范围适配器是有条件借用的 (P2017)。也就是说,它们不会在它们正在适应的范围之上添加任何额外的状态——因此如果基础范围是(或基础范围是),则可以借用适应的范围。例如,每当借用 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:

If E is an rvalue and enable_­borrowed_­range<remove_­cv_­t<T>> is false, ranges​::​begin(E) is ill-formed.

因此,因为您的原始代码已编译,所以您可以确定它是有效的。