在https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html上,建议编写如下循环:
Container::iterator end = large.end();
for (Container::iterator it = large.begin(); it != end; ++it) {
//...;
}
而不是
for (Container::iterator it = large.begin(); it != large.end(); ++it) {
//...;
}
由于我很少在任何代码中看到这种风格,我想知道end()的连续调用是否真的为stl容器上的大型循环添加了明显的运行时开销,或者编译器是否已经优化了这种情况。 / p>
编辑: 正如许多非常好的评论所指出的那样:只有当循环中的代码不修改结束迭代器时,此问题才有效。否则,当然必须重复调用结束。
答案 0 :(得分:18)
C ++ 11标准(第23.2.1节)要求end
具有O(1)复杂度,因此符合标准的实现对两个版本都具有相同的性能特征。
那就是说,除非编译器能够证明end
的返回值永远不会改变,否则将end
拉出循环可能更快一些不变量(正如Steve Jessop评论的那样,有很多变量可以影响这是否真实。)
尽管如此,即使在一个特定情况下绝对没有性能差异,将这些测试拉出循环是一个很好的习惯。进入的甚至更好的习惯是使用标准算法,如@pmr所说,它完全回避了这个问题。
答案 1 :(得分:7)
这不是关于end
成本高的问题,而是关于编译器看到end
不会通过循环体中的副作用(它是循环不变量)而改变的能力。
标准要求end
的复杂性保持不变。请参阅23.2.1
中N3337中的表96。
使用标准库算法很好地避免了整个困境。
答案 2 :(得分:3)
如果您计划在迭代时修改集合,则必须以第二种方式进行修改(结束可以更改) - 否则第一种方法在理论上会快一点。我怀疑它会引人注意。
答案 3 :(得分:2)
实际上,end()方法是内联的。第二个不是每次都调用它,我不认为end()给出任何性能滞后。
答案 4 :(得分:0)
std :: vector.end()(for example)按值返回迭代器。在第二个循环中,您将在每个循环中创建一个对象。如果您不需要,编码标准告诉您不要创建对象。编译器可能是智能的,并为您优化代码,但这不是保证。更好的解决方案是使用stl算法。它们已经过优化,您的代码将更具可读性。请注意,只有在未修改集合时,这两个循环才是等效的。
P.S。很可能性能上的差异很小
答案 5 :(得分:0)
对于现在读这篇文章的人来说,这个问题在C ++ 11中已经变得有些问题了。
我不确定这种回答是否有资格作为答案,因为它实际上并没有解决问题的重点。但我确实认为指出这里提出的问题在C ++ 11程序员的实践中很少遇到是有效的,而且我肯定会在几年前发现这个响应很有用。因此,此响应针对的是只想了解迭代浏览STL容器中所有元素的最佳方式的读者(vector
,list
,deque
等等。)。
假设OP想要访问容器中的每个元素,我们可以通过编写range-based for loop轻松回避定义end
是否比调用Container::end()
足够快的整个问题:
Container container; // my STL container that has been filled with stuff
// (note that you can replace Container::value_type with the value in the container)
// the standard way
for (Container::value_type element : container) {
// access each element by 'element' rather than by '*it'
}
// or, if Container::value_type is large
Container container; // fill it with something
for (Container::value_type& element : container) {
//
}
// if you're lazy
Container container; // fill it with something
for (auto element : container) {
//
}
OP已经询问在每次迭代时简单地比较it
和Container::end()
的简洁程度与声明变量end
的性能之间的权衡以及在每个步骤中进行比较是值得的。由于基于范围的for循环提供了一个简单,易于编写和易于阅读的替代方案,在内部也会发生end
迭代器,而不是在每一步都调用Container::end()
方法,我们需要纠缠于这个问题的案例已经减少到有限的案例。
根据cppreference.com,基于范围的for循环将产生具有与以下相同副作用的代码:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
答案 6 :(得分:-3)
取决于实施,但我认为end()
没有给出那么多的性能滞后。