Qua*_*um7 46 c++ compiler-construction optimization iterator coding-style
我看到很多c ++代码看起来像这样:
for( const_iterator it = list.begin(),
const_iterator ite = list.end();
it != ite; ++it)
Run Code Online (Sandbox Code Playgroud)
与更简洁的版本相反:
for( const_iterator it = list.begin();
it != list.end(); ++it)
Run Code Online (Sandbox Code Playgroud)
这两个约定之间的速度会有什么不同吗?由于list.end()只被调用一次,因此第一个会稍快一些.但由于迭代器是const,似乎编译器会将此测试从循环中拉出来,为两者生成等效的汇编.
new*_*cct 43
但这两个版本并不相同.在第二个版本中,它list.end()
每次都会比较迭代器,并且list.end()
评估的内容可能会在循环过程中发生变化.当然,你无法list
通过const_iterator进行修改it
; 但是没有什么能阻止循环中的代码list
直接调用方法并对其进行变异,这可能(取决于什么类型的数据结构list
)改变结束迭代器.因此在某些情况下预先存储结束迭代器可能是不正确的,因为到达它时它可能不再是正确的结束迭代器.
j_r*_*ker 29
我就提备案的C++,调用标准要求begin()
,并end()
在任何容器类型(可能是vector
,list
,map
等)必须采取唯一不变的时间.实际上,如果在启用优化的情况下进行编译,则几乎肯定会将这些调用内联到单个指针比较中.
请注意,此保证不一定适用于其他供应商提供的"容器",这些容器实际上不符合标准第23章(例如单链表slist
)中规定的容器的形式要求.
我会选择最简洁和可读的选项.不要试图再次猜测编译器及其可能执行的优化.请记住,绝大多数代码对整体性能没有任何影响,因此,只有在性能关键的代码段中,您才应该花时间对其进行分析并选择适当有效的源代表.
具体参考您的示例,第一个版本生成迭代器的副本end()
,调用为迭代器对象的复制构造函数运行的任何代码.STL容器通常包含内联end()
函数,因此编译器有很多机会优化第二个版本,即使您没有尝试帮助它.哪一个最好?测量它们.
您可以使第一个版本更简洁,并充分利用两者:
for( const_iterator it = list.begin(), ite = list.end();
it != ite; ++it)
Run Code Online (Sandbox Code Playgroud)
PS迭代器不是const,它们是const引用的迭代器.有很大的不同.
考虑这个例子:
for (const_iterator it = list.begin(); it != list.end(); ++list)
{
if (moonFull())
it = insert_stuff(list);
else
it = erase_stuff(list);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,你需要在循环中调用list.end(),编译器不会优化它.
在编译器可以证明end()总是返回相同值的其他情况下,可以进行优化.
如果我们讨论的是STL容器,那么我认为任何好的编译器都可以在编程逻辑不需要多次end()调用时优化多个end()调用.但是,如果您有自定义容器并且end()的实现不在同一个转换单元中,那么优化必须在链接时进行.我对链接时间优化知之甚少,但我敢打赌,大多数链接器都不会进行这样的优化.