c++ for循环:条件的计算

C++ for loop: evaluation of condition

本文关键字:计算 条件 for 循环 c++      更新时间:2023-10-16

我有一个(傻吗?)关于循环的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);
}

第二个版本更好。在第一个中,每次都计算条件(没有自动假设结果是不变的)。

是。第二个版本更好。

对于第一个版本,如果容器类型astd::vector beginenda的迭代器,则push_back操作可能会导致vector的大小调整,从而使beginend迭代器失效,并且在下一次迭代中使用它们来计算距离将调用未定义行为。在这种情况下,第二种方法不仅更好,而且定义良好

如果没有优化,它实际上每次都是计算的。在优化的实践中,它不应该有什么不同。如果它不是每次都计算(例如,因为这是你最内在的事情)是至关重要的,你总是可以保险起见,选择第二个。

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

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


EDIT 我的回答假设,您的代码中的beginend而不是向量(或无论它是什么)abeginend。如果是,那么你的问题的答案取决于你实际想做什么。

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

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

与之相反,vector迭代器的检查实现在理论上可能包括检查自迭代器被获取后vector是否被重新分配(因此迭代器不再有效)的代码。这个事实可以在循环中更改,因此如果std::distance间接调用该检查,那么在该实现中它不能被提升。并不是说你通常会把检查迭代器和优化结合起来,但这是可能的。

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

无论如何,对于第二个代码片段有些人更喜欢这种风格:

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

它依赖于比较中的类型是相同的,但它避免将dist放入周围的作用域,在那里它不需要。

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