在修改底层容器时,我没有找到任何对 range/range-adaptor/range-view 特定失效规则的直接引用。
\n\n直觉表明它与指针/迭代器失效规则完全相同——这些规则在标准的容器部分中指定。
\n\n目前容器失效的措辞如下:
\n\n\n\n\n“...使引用序列中元素的所有引用、指针和迭代器以及尾后迭代器无效。”
\n
这就提出了一个问题:所有范围是否都必须“引用序列的元素”,或者它们是否可以通过容器的接口访问元素?
\n\n在我看来,大多数范围适配器已经访问了一个序列,而不直接引用该序列的元素(即惰性视图只是构建迭代器适配器)。
\n\n可以这么说,似乎重要的是视图金字塔底部的基本范围。
\n\n我们都在某个时刻了解到,在迭代同一个向量时不能这样做std::vector::push_back,因为内存可能会移动并使迭代无效。但是,我们还了解到,您可以通过 push_back使用std::vector::operator[]访问权限,只要您仔细检查您的size()。
在我看来,相同的规则适用于范围/适配器/视图。
\n\n那么:是否可以强制随机访问容器上的某些等效项std::ranges::views\xe2\x80\x8b::\xe2\x80\x8ball(或者也许是take_view)使用数组索引(或某些等效的间接/惰性元素访问),而不直接使用迭代?
允许这样做的东西:
\n\nstd::vector<People> people = ...;\nfor (auto& person : std::ranges::views::lazy_all(people)) { // or ranges::lazy_take_view(people, people.size())\n if (person.has_new_child()) {\n people.push_back(person.get_new_child());\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n
我目前正在使用 C++20 范围,在实现我自己的视图时,我提出了同样的问题:视图的迭代器失效的规则是什么?
据我所知,范围大量使用元编程,并在幕后构建了状态机的层次结构。这些状态机的实际类型通常是隐藏的[1],因此我们可能很难假设它们的限制。迭代器失效是这些限制的一部分,因此在构造视图层次结构时指定迭代器何时以及如何失效即使不是不可能,也可能非常具有挑战性。即使我们设法描述这些规则,它们也可能无法记住,更不用说有效使用了。
Ranges V3 库有以下建议:
查看有效性
对基础范围进行的任何使其迭代器或标记无效的操作也将使引用该范围任何部分的任何视图无效。此外,当范围的底层元素发生变化时,某些视图(例如,views::filter)将失效。最好在任何可能改变底层范围的操作之后重新创建视图。
https://github.com/ericniebler/range-v3/blob/master/doc/index.md
上面给出的限制只是消除了所有问题,尽管它比标准容器的规则更严格,但它建立了一个简单的规则来记住任何视图迭代器失效。同时,它提供了更改视图实现的自由,而无需触及该规则。
因此,我认为,可以安全地假设 C++20 标准中的范围受到相同的限制。
[1] 我的观察基于范围的 MSVC 实现,其中范围适配器实际上可以根据策略生成不同的类型。因此,例如,当您使用管道 std::views::take() 时,您可能会突然以 std::span() 结束。