C ++ for循环:条件评估

时间:2011-10-28 10:38:26

标签: c++ performance for-loop conditional-statements

我有一个关于循环的(愚蠢?)C / C ++问题:

for (size_t i = 0; i < std::distance(begin, end); ++i) {
  a.push_back(i);
}

beginend是两个迭代器。我的问题是,是否为循环中的每个元素计算了std::distance(begin, end)?或者使用此版本更好:

size_t dist = std::distance(begin, end);
for (size_t i = 0; i < dist; ++i) {
  a.push_back(i);
}

6 个答案:

答案 0 :(得分:6)

第二版更好。在第一个中,每次都评估条件(没有关于结果不变的自动假设)。

答案 1 :(得分:3)

是。第二个版本更好。

对于第一个版本,如果容器类型astd::vector,则 beginend是{{1}的迭代器然后a操作可能会导致向量调整大小,这反过来会使push_backbegin迭代器无效,并使用它们来计算下一次迭代中的距离将调用未定义的行为。在这种情况下,第二个不仅更好,而且定义良好

答案 2 :(得分:2)

最有可能的是,这取决于编译器的优化。如果它们被关闭,std::distance将在每个循环上执行,这是肯定的。原因 - 可以在循环体内更改迭代器。

所以,如果你没有改变迭代器,那么更喜欢第二个版本,甚至认为它是非常小的优化。
在大多数情况下,这是个人选择的问题(如果这不是瓶颈,这是非常不可能的)。


编辑 我的回答是,代码中的beginend {{1向量的{}}和begin enda。如果是,那么你的问题的答案取决于你实际上想要做什么。

答案 3 :(得分:2)

没有优化,每次都会真正计算出来。在优化实践中,它不应该有所作为。如果每次都不计算它是至关重要的(例如因为这是你最内心的事情),你可以随时安全地使用它并与第二个一起使用。

答案 4 :(得分:1)

编译器是否可以执行优化取决于(至少)涉及的类型。例如,如果beginend是列表迭代器,a是列表,enda末尾的迭代器,那么这是一个无限循环(直到出现内存不足)。如果编译器改变了程序的含义,则编译器无法进行“优化”。据推测,它不会改变程序的含义,或者你不会问这个问题,但编译器仍然必须以某种方式排除这种可能性,例如通过从设置的任何地方跟踪end的值。在某些情况下,对你来说可能是显而易见的,但超出了编译器的力量来证明。

相反,如果beginend是向量迭代器,那么在实践中它们要么是一个指针,要么是一个薄包装器。然后编译器可能会看到它们的值在循环中永远不会改变,因此它们的距离永远不会改变,并进行优化。尽管它没有进行优化,但distance对于矢量迭代器来说是便宜的。因此,在这种情况下,优化可能并不那么重要。

相反,向量迭代器的检查实现理论上可能包含代码,以查看自迭代器被采用后是否已重新分配向量(因此迭代器不再有效)。这个事实可以在循环中发生变化,因此如果std::distance间接调用该检查,那么在该实现上它就不能被提升。并不是说您通常会将检查迭代器与优化结合起来,但这是可能的。

与以往一样,优化依赖于实现。如果你真的在乎你需要查看你关心的编译器发出的代码。

对于它的价值,对于你的第二个片段,有些人更喜欢这种风格:

for (size_t i = 0, dist = std::distance(begin, end); i < dist; ++i) {
  a.push_back(i);
}

它依赖于比较中的类型是相同的,但它避免将dist放入周围的范围,而不需要它。

答案 5 :(得分:0)

两者都具有与任何体面的编译器将代码转换为第二版本相同的性能。如果关闭IF编译器优化,请使用第二个版本。