缓存最终迭代器 - 好主意还是坏主意?
一般来说,出于效率和速度的目的,缓存结束迭代器(特别是 STL 容器)是一个好主意吗?比如下面的代码:
std::vector<int> vint;
const std::vector<int>::const_iterator end = vint.end();
std::vector<int>::iterator it = vint.begin();
while (it != end)
{
....
++it;
}
在什么情况下最终值会失效?从容器中擦除会导致所有 STL 容器中的 end 失效还是仅在某些容器中失效?
Generally speaking is it a good idea to cache an end iterator (specifically STL containers) for efficiency and speed purposes? such as in the following bit of code:
std::vector<int> vint;
const std::vector<int>::const_iterator end = vint.end();
std::vector<int>::iterator it = vint.begin();
while (it != end)
{
....
++it;
}
Under what conditions would the end value be invalidated? would erasing from the container cause end to be invalidated in all STL containers or just some?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
在
vector
的简单情况下,当您从容器中添加或删除元素时,end
迭代器将发生变化;不过,通常最安全的假设是,如果在迭代容器时改变容器,则它的所有迭代器都会变得无效。在任何给定的 STL 实现中,迭代器的实现方式可能不同。关于缓存
end
迭代器——缓存它当然是有效的,但要了解它在您的情况下是否实际上更快,最好的办法是分析您的代码并查看。虽然从vector
检索end
迭代器可能是使用最新的 STL 库和编译器的快速实现,但我曾经参与过过去的项目,其中缓存end
迭代器给我们带来了显着的速度提升。 (这是在 PlayStation 2 上进行的,所以请持保留态度。)In the simple case of a
vector
, theend
iterator will change when you add or remove elements from the container; though, it's usually safest to assume that if you mutate the container while iterating over it, all iterators to it become invalid. Iterators may be implemented differently in any given STL implementation.With regard to caching the
end
iterator -- it's certainly valid to cache it, but to find out if it is actually faster in your case, the best bet is for you to profile your code and see. While retrieving theend
iterator from avector
is likely a fast implementation with a recent STL library and compiler, I have worked on past projects where caching theend
iterator gave us a significant speed boost. (This was on the PlayStation 2, so do take with a grain of salt.)如果我们谈论效率和速度:由于编译器优化和内联,缓存结束迭代器是不必要的。
If we are talking about efficiency and speed: caching the end iterator is unnecessary because of compiler optimizations and inlining.
从当前正在迭代的容器中删除始终是一个坏主意。最终迭代器的实际缓存不会改变这一点。
h.
Erasing from a container over which you are currently iterating is always a bad idea. The actual caching of your end iterator is not going to change that.
h.
如果您使用 STL 容器算法,则无论如何都会发生结束迭代器的缓存(当您将 container.end() 的结果作为参数传递时)。
如果您修改容器的内存(插入/删除元素),这是一个坏主意。
此外,为了提高效率而进行缓存几乎没有多大意义:在大多数情况下,end() 是由编译器内联的,如果不是,则您的效率很可能不会取决于缓存的 end() 结果。不过,YMMV。
If you use the STL container algorithms the caching of the end iterator is happens anyway (as you pass the result of container.end() as a parameter).
If you modify the memory of the container (insert/remove elements) it's a bad bad idea.
Also, caching for efficiency rarely makes much sense: in most cases the end() is inlined by the compiler, and when it is not, it is very probable that your efficiency doesn't hang on the end() result being cached. YMMV though.
对于每种类型的容器,都非常明确地定义了失效规则(对于迭代器)。我发现 SGI 网站非常有用 http://www.sgi.com/tech/stl /table_of_contents.html
特别针对我发现的向量:
The invalidation rules (for iterators) is defined very explicitly for each type of container. I find the SGI site very useful http://www.sgi.com/tech/stl/table_of_contents.html
Specifically for vectors I find:
我经常使用这种样式来迭代容器:
从向量中删除元素会使删除位置之后的所有迭代器无效。
我不确定其他容器类型。无论如何,我认为可以安全地假设所有迭代器在擦除后都变得无效。如果您确实需要非常具体的信息,那么您可以随时查找。我很少需要这个,因为我的编码风格相当保守。
I often use this style for iterating containers:
Erasing an element from a vector invalidates all iterators after the erased position.
I'm not certain about the other container types. In any case I think it would be safe to assume that all iterators become invalid after an erase. If you really need very specific information then you can always look it up. I rarely need this because because of my rather conservative coding style.
一般来说,如果缓存结束迭代器应该没有关系。如果您认为这确实很重要,那么您应该已经在代码上使用分析器并且能够分析这两种变体。我怀疑它可能会根据容器类型而有所不同 - 但分析器将是确定您的编译器、优化和 STL 供应商的唯一方法。
In general it should not matter if you cache the end iterator. If you feel it does matter, then you should already be using a profiler on your code and would be able to profile both variations. I'd suspect it could differ depending upon the container type - but a profiler would be the only way to know for sure given your compiler, optimizations and STL vendor.
这真的非常取决于您在
...
代码中做什么。如果编译器可以证明
vint.end()
不会改变,那么这可能并不重要,但你会受到编译器优化的支配,并且有多清晰。 .
代码。您的方法是对编译器帮助最大的方法,您承诺
end
迭代器不会失效,并且您不会修改end
中的元素(无论如何都是无效的)。对于您在...
中承诺要做的事情,您再明确不过了。现在是 2019 年,for-range 循环基本上等同于您的代码: https: //en.cppreference.com/w/cpp/language/range-for
顺便说一句,这意味着如果您不是真正对
it
感兴趣,而是对*it
感兴趣code>,你可以结束(没有双关语)这个困境,只需写:It really, really depends of what are you doing in the
...
code.If the compiler can prove that
vint.end()
is not going to change then it might not matter, but you are at the mercy of the compiler optimizations then and how clear is the...
code.Your approach is the one that helps the compiler the most, you are promising that the
end
iterator will not be invalidated and that you are not going to modify the element inend
(which is invalid any way). You cannot be more explicit than this about what you promise you will do in...
.It is 2019, and for-range loops are basically equivalent to your code: https://en.cppreference.com/w/cpp/language/range-for
which incidentally means that if you were not really interested in
it
, but in*it
, you can put an end (no pun intended) to the dilemma and just write: