简体   繁体   中英

C++ for loop: evaluation of condition

I have got a (stupid?) C / C++ question about a loop:

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

begin and end are two iterator. My question is, is std::distance(begin, end) calculated for each element in the loop? Or is it better to use this version:

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

Second version is better. In the first one the condition is evaluated each time (no automatic asumption about the result being invariant).

Yes. The second version is better.

As for the first version, if the container type a is std::vector , and begin and end are iterators of a , then the push_back operation might cause resizing the vector, which in turn will invalidate begin and end iterators, and using them to calculate the distance in the next iteration will invoke undefined behaviour . In this case, the second is not only better, it is well-defined as well.

Most likely, this depends on the compiler optimizations. IF they are turned off, the std::distance will be executed on each loop, and this is for sure. The reason - the iterators may be changed inside the loop's body.

So, if you're not changing the iterators, prefer the second version, even thought it's very small optimization.
In most cases, it's a matter of personal choice ( if this is not a bottle neck, which is very unlikely).


EDIT My answer assumes, that begin and end in your code are NOT the begin and end of the vector (or whatever it is) a . If they are, then the answer to your question depends on what you're actually trying to do.

Without optimization, it is really computed every time. In practice with optimization, it shouldn't make a difference. If it is crucial that it isn't calculated every time (eg because this is your most inner thing to do), you can always play it safe and go with the second one.

Whether the compiler can perform the optimization depends (at least) on the types involved. For example, if begin and end are list iterators, and a is a list, and end is an iterator at the end of a , then this is an infinite loop (until out-of-memory occurs). The compiler cannot make the "optimization" if it changes the meaning of the program. Presumably it doesn't change the meaning of the program or you wouldn't ask the question, but still the compiler must somehow rule out that possibility, for example by tracking the value of end from wherever it was set. In some cases it might be obvious to you, but beyond the power of the compiler to prove.

Conversely, if begin and end are vector iterators, then in practice they're either a pointer or a thin wrapper around one. Then the compiler might well be able to see that their values never change in the loop, and hence their distance never changes, and make the optimization. Even if it doesn't make the optimization, though, distance is cheap for vector iterators. So the optimization may not be all that significant anyway in that case.

Con-conversely, a checking implementation of a vector iterator might in theory include code to see whether the vector has been reallocated since the iterator was taken (and hence the iterator is no longer valid). This fact can change in the loop, so if std::distance indirectly invokes that check, then on that implementation it can't be hoisted. Not that you'd usually combine a checking iterator with optimization, but it's possible.

As ever, optimization is implementation-dependent. If you really care you need to look at the code emitted by the compiler(s) you care about.

And for what it's worth, for your second snippet some people prefer this style:

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

It relies on the types in the comparison being the same, but it avoids putting dist into the surrounding scope, where it doesn't need to be.

Both will have the same performance as any decent compiler will convert the code into second version. IF compiler optimization is turned off, use the second version.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM