06 - loop control
Apart from break
, there is one more statement that can control the loop.
Continue statement
break
jumps outside of the loop. continue
jumps to the end of current iteration (step statement is still executed).
The example below prints combinations of numbers but only if they are different.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { for (int j = 0; j < 4; ++j) { for (int i = 0; i < 4; ++i) { if (i == j) continue; std::cout << j << "-" << i << ", "; } } } |
The same behavior could be achieved with if
statements alone, but continue
is better because only this keyword is nested inside if
, not the whole loop body. Less nesting generally means more readable code.
Exiting nested loops
break
can be used to exit a loop (the most enclosing one). But how to exit nested loops? Some languages offer such feature by extending syntax of the break
keyword, usually by allowing labels or numbers (how many scopes to exit). There is no such thing in C++.
There are multiple alternatives:
Use
goto
. Heavily discouraged because it breaks structured programming model.throw
an exception. Heavily discouraged because exceptions should be used only for error handling, never for control flow.Use a
bool
variable as a flag.Exit the entire function by a
return
statement.
Because of the mentioned rationale, only last 2 are used in practice.
Flag variable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
bool exit = false; for (int y = 0; y < size_y; ++y) { if (exit) break; for (int x = 0; x < size_x; ++x) { if (/* ... */) { exit = true; break; } } } |
It works, but has multiple disadvantages:
An extra variable is needed, which complicates code.
Multiple
break
andif
statements might be needed, which is another complication and a good source of potential bugs.The code has so many (unpredictable) relations between loop control variables and the flag that it might be hard to optimize.
In practice this way of dealing with nested loops is rarely used - usually only when due to program logic the loops already have to contain a flag variable and it can be reused.
Return statement
The preferred option, results in very clean code but it exits a lot more - the entire function.
1 2 3 4 5 6 7 8 9 10 |
for (int y = 0; y < size_y; ++y) { for (int x = 0; x < size_x; ++x) { if (/* ... */) { return /* ... */; } } } |
In practice this is not a problem because loops can be easily moved to a separate function (to alter the scope of return statement), especially local function objects created by lambda expressions. Once you learn lambdas, you will understand how useful they are.