Does a C++11 Range-Based for Loop Condition Get Evaluated Every Cycle

Does a C++11 range-based for loop condition get evaluated every cycle?

It is only evaluated once. The standard defines a range-based for statement as equivalent to:

{
auto && __range = range-init;
for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}

where range-init is the expression (surrounded by parentheses) or braced-init-list after the :

C++11 range-based for() loops evaluate once or multiple times?

It is evaluated only once. The standard says that the range-based for loop is equivalent to this:

§6.5.4 The range-based for statement [stmt.ranged]

{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}

with range-init being equivalent to ( bar() ) in your case (the expression you specify, surrounded by parenthesis). That expression is only evaluated once as you can see.

How do Range-Based for loops handle temporary containers

get_vector() is evaluated once, and the result is stored in a temporary.

6.5.4/1 ...a range-based for statement is equivalent to

{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}

In your example, range-init would be (get_vector()).

Is statement in for each loop executed each iteration?

The range-based for loop is equivalent as follows:

{
auto && __range = range_expression ;
auto __begin = begin_expr ;
auto __end = end_expr ;
for ( ; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}

Note the 1st statement auto && __range = range_expression ; (range_expression will be GenerateRandomContainer() for your code); that means the Container will be generated only once, and iterates on all the elements of it.

Is the expression in a ranged-for statement evaluated on each iteration?

No, only once.

[C++11: 6.5.4]: For a range-based for statement of the form

for ( for-range-declaration : expression ) statement

let range-init be equivalent to the expression surrounded by parentheses

( expression )

[..] a range-based for statement is equivalent to

{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}

[..]

Interestingly, this relatively new passage is a rare example of the standard defining semantics in terms of code using pre-existing language features. I guess they got bored of writing terse technical language.

It means we have to deduce the fact above from the code; in this case, fortunately, it's easy: range-init is evaluated once and bound to the universal reference __range.

Is c++11 range-for not equal to classic for in terms of functionality?

First loop prints the message 10 times, second one prints it only once.

That's right, because in the first loop you've asked the compiler to create a new Range to compare the iterator to each time you enter the loop.

Are they not equal?

No

Can this difference complicate old code revision?

Not usually. It's just that you've created a contrived example.

In a loop, do any operations in the end-condition get evaluated in every iteration?

It obviously depends on the language. For JavaScript, the spec (ECMAScript §12.6.3) requires it always be evaluated each time. As an optimization, a specific JavaScript runtime could skip one or more of the length calls, if it could prove that the result would not change.

How can I avoid for loops with an if condition inside them with C++?

IMHO it's more straight forward and more readable to use a for loop with an if inside it. However, if this is annoying for you, you could use a for_each_if like the one below:

template<typename Iter, typename Pred, typename Op> 
void for_each_if(Iter first, Iter last, Pred p, Op op) {
while(first != last) {
if (p(*first)) op(*first);
++first;
}
}

Usecase:

std::vector<int> v {10, 2, 10, 3};
for_each_if(v.begin(), v.end(), [](int i){ return i > 5; }, [](int &i){ ++i; });

Live Demo

Is it safe to use a C++11 range-based for-loop with an rvalue range-init?

Yes, it's perfectly safe.

From [class.temporary]/4-5:

There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when a default constructor is called [...]

The second context is when a reference is bound to a temporary. The temporary to which the reference is
bound or the temporary that is the complete object of a subobject to which the reference is bound persists
for the lifetime of the reference
except:

  • A temporary bound to a reference member in a constructor’s ctor-initializer [...]
  • A temporary bound to a reference parameter in a function call [...]
  • The lifetime of a temporary bound to the returned value in a function return statement [...]
  • A temporary bound to a reference in a new-initializer [...]

None of those exceptions apply. The temporary thus persists for the lifetime of the reference, __range, which is the entire loop.

C for loop implemented differently than other languages?

Consider this:

for i:=0 to 100 do { ... }

In this case, we could replace the final value, 100, by a function call:

for i:=0 to final_value() do { ... }

... and the final_value-function would be called only once.

In C, however:

for (int i=0; i<final_value(); ++i) // ...

... the final_value-function would be called for each iteration through the loop, thus making it a good practice to be more verbose:

int end = final_value();
for (int i=0; i<end; ++i) // ...


Related Topics



Leave a reply



Submit