Skip to content

Flow control

  • if
  • else
  • else if
  • while (true)
  • continue
  • break
  • do ... while
if (true)
{
std::cout << "Hello" << std::endl;
}
Answer

true in a condition causes the body of the if statement to always execute.

if (false)
{
std::cout << "Hello" << std::endl;
}
Answer

The body of the if statement is guaranteed not to execute.

bool execute = true;
if (execute)
{
std::cout << "Hello" << std::endl;
}
bool notExecute = !execute;
if (notExecute)
{
std::cout << "Not executed" << std::endl;
}
Answer

When run, only Hello will be printed.

if (1)
{
std::cout << "1" << std::endl;
}
if (123)
{
std::cout << "123" << std::endl;
}
if (0)
{
std::cout << "0" << std::endl;
}
Answer

In C++, if accepts any expression that can be converted to bool. This code will compile and print 1 and 123.

int* a { nullptr };
if (a)
{
std::cout << "a is true" << std::endl;
}
int b {};
int* pb { &b };
if (pb)
{
std::cout << "pb is true" << std::endl;
}
if (b)
{
std::cout << "b is true" << std::endl;
}
bool c { false };
bool* pc { &c };
if (pc)
{
std::cout << "pc is true" << std::endl;
}
Answer

When the condition is a pointer, if checks whether it is non-null (not nullptr). a is true will not be printed here, while pb is true and pc is true will be printed.

if (b) is equivalent to if (0), which is equivalent to if (false). b is true will not be printed.

if (false)
{
std::cout << "A" << std::endl;
std::cout << "B" << std::endl;
}
Answer

Nothing will be printed.

if (false)
std::cout << "A" << std::endl;
std::cout << "B" << std::endl;
Answer

Only B will be printed because only the first statement is attached to the if.

Equivalent code:

if (false)
{
std::cout << "A" << std::endl;
}
std::cout << "B" << std::endl;
if (false)
{
std::cout << "A" << std::endl;
}
else
{
std::cout << "B" << std::endl;
}
Answer

This code will print B, since the else block executes when the condition is not satisfied.

9. Reassigning a condition variable inside the if body

Section titled “9. Reassigning a condition variable inside the if body”
bool a = true;
if (a)
{
a = false;
}
else
{
std::cout << "B" << std::endl;
}
Answer

B will not be printed because whether else executes is determined when a is checked in the if, which happens before it is changed.

void F()
{
if (true)
{
return;
}
else
{
std::cout << "Else" << std::endl;
}
std::cout << "After Else" << std::endl;
}
Answer

Else will not be printed here. Whether the else block executes depends only on the condition in the if.

return exits the function immediately, so After Else will not be printed either.

if (true)
std::cout << "A" << std::endl;
else
std::cout << "B" << std::endl;
std::cout << "C" << std::endl;
Answer

B will not be printed. A and C will be printed.

if (true)
std::cout << "A" << std::endl;
else
{
std::cout << "B" << std::endl;
}
Answer

It is valid to use an unbraced statement after if and an explicit block after else.

How would this code usually be written using an if-else chain?

if (a)
{
std::cout << "A" << std::endl;
}
else
{
if (b)
{
std::cout << "B" << std::endl;
}
else
{
if (c)
{
std::cout << "C" << std::endl;
}
}
}
Answer

An if statement is usually placed directly after else:

if (a)
{
std::cout << "A" << std::endl;
}
else if (b)
{
std::cout << "B" << std::endl;
}
else if (c)
{
std::cout << "C" << std::endl;
}

Try to represent this code as an equivalent if-else chain. How can this code be written using an early return / guard clause?

if (a)
{
std::cout << "A" << std::endl;
}
else
{
std::cout << "After A" << std::endl;
if (b)
{
std::cout << "B" << std::endl;
}
else
{
std::cout << "After B" << std::endl;
if (c)
{
std::cout << "C" << std::endl;
}
else
{
std::cout << "After C" << std::endl;
}
}
}
Answer (chain)

This code cannot be represented as an if-else chain without duplicating code. There is nowhere to put After B and After C so that they execute according to the same rules without duplicating them. You could duplicate them, as shown below, but that would make the code harder to maintain:

if (a)
{
std::cout << "A" << std::endl;
}
else if (b)
{
std::cout << "After A" << std::endl;
std::cout << "B" << std::endl;
}
else if (c)
{
std::cout << "After A" << std::endl;
std::cout << "After B" << std::endl;
std::cout << "C" << std::endl;
}
else
{
std::cout << "After A" << std::endl;
std::cout << "After B" << std::endl;
std::cout << "After C" << std::endl;
}
Answer (guard clause / early return)
  1. Put this code in a function.
  2. Add a return statement to each if block.
  3. Then the else statements and blocks can be removed.
#include <iostream>
void f(bool a, bool b, bool c)
{
if (a)
{
std::cout << "A" << std::endl;
return;
}
std::cout << "After A" << std::endl;
if (b)
{
std::cout << "B" << std::endl;
return;
}
std::cout << "After B" << std::endl;
if (c)
{
std::cout << "C" << std::endl;
return;
}
std::cout << "After C" << std::endl;
}
int main()
{
// предполагается, что a, b, c были созданы ранее ...
f(a, b, c);
}
Why use this guard clause / early return?
  • To move error handling to the top of the function and the main logic below it. This makes it clear that the main logic depends on the conditions checked during error handling (the contract).
  • It removes unnecessary nesting of conditions;
  • It promotes locality between error checks and their handling.

Example code without using a guard clause / early return:

void sendWelcomeEmail(User* user)
{
// Условия перечислены с увеличением вложенности.
if (user != nullptr)
{
if (user->IsActive)
{
if (user->EmailConfirmed)
{
// Код с самим действием спрятан в середине функции.
std::cout << "Sending email to " << user->email << std::endl;
}
else
{
std::cout << "Email not confirmed." << std::endl;
}
}
else
{
std::cout << "User is not active." << std::endl;
}
}
// Не соблюдается локальность:
// обработка удалена в исходном коде от связанной проверки.
else
{
std::cout << "User not found." << std::endl;
}
}

The same code using a guard clause / early return:

void sendWelcomeEmail(User* user)
{
// Можно блоком разграничить контракт
// (необходимые условия для выполнения основного действия),
// или вынести его в свою функцию.
{
// Соблюдена локальность: условия рядом с их обработкой.
if (user == nullptr)
{
std::cout << "User not found." << std::endl;
return;
}
if (!user->isActive)
{
std::cout << "User is not active." << std::endl;
return;
}
if (!user->emailConfirmed)
{
std::cout << "Email not confirmed." << std::endl;
return;
}
}
// Основной код находится после всех проверок, а не в середине.
std::cout << "Sending email to " << user->email << std::endl;
}
int i = 0;
while (true)
{
if (i == 4)
{
std::cout << "ERROR: Should not happen" << std::endl;
break;
}
if (i == 3)
{
std::cout << "Exit" << std::endl;
break;
}
if (i == 0)
{
std::cout << "Increase by 2 on first iter" << std::endl;
i += 2;
continue;
}
std::cout << "Increase by 1 normally" << std::endl;
i++;
}
What do break and continue do?

break stops the loop; execution continues with the first statement after it.

continue starts the next iteration of the loop (the remaining statements in the loop body are not executed for that iteration).

Answer

“Increase by 2 on first iter” will be printed in the first loop iteration; i++ will not execute because continue skips it.

“Increase by 1 normally” will be printed in the second loop iteration, after all if checks have failed.

“Exit” will be printed in the third iteration, while the i == 0 check and the i++ statement will not execute, because break stops the loop.

Increase by 2 on first iter
Increase by 1 normally
Exit
int F()
{
while (true)
{
if (true)
{
return 0;
}
break;
}
return 1;
}
Answer

The function will return 0. break and return 1 will not execute.

#include <iostream>
#include <cassert>
struct Result
{
bool isDenominatorZero;
// Should only be read if there is no error.
int result;
};
Result ceilingDivide(int numerator, int denominator)
{
if (denominator == 0)
{
return {
.isDenominatorZero = true,
.result = 0,
};
}
int value = (numerator + denominator - 1) / denominator;
return {
.isDenominatorZero = false,
.result = value,
};
}
int main()
{
{
int totalStudents { 25 };
int studentsPerTable { 2 };
Result result { ceilingDivide(totalStudents, studentsPerTable) };
if (!result.isDenominatorZero)
{
int numTablesNeeded = result.result;
std::cout << "Number of tables needed is " << numTablesNeeded << std::endl;
}
else
{
std::cout << "Can't seat any students if no students are seated per table" << std::endl;
assert(studentsPerTable == 0);
}
}
{
Result result { ceilingDivide(30, 0) };
if (result.isDenominatorZero)
{
std::cout << "Could not divide" << std::endl;
}
else
{
std::cout << "The result is " << result.result << std::endl;
}
}
}

Refactor this code using a guard clause / early return.

#include <iostream>
int main()
{
int number = 10;
if (number == 5)
{
std::cout << "The number is 5" << std::endl;
}
else
{
std::cout << "The number is not 5" << std::endl;
if (number % 2 == 1)
{
std::cout << "The number is not divisible by 2" << std::endl;
}
else
{
std::cout << "The number is divisible by 2" << std::endl;
if (number == 6)
{
std::cout << "The number is 6" << std::endl;
}
}
}
}

Here is the starter code:

#include <iostream>
int main()
{
int apples;
int pears;
int oranges;
std::cout << "Apples:";
std::cin >> apples;
std::cout << "Pears:";
std::cin >> pears;
std::cout << "Oranges:";
std::cin >> oranges;
// ...
return 0;
}
  • If the number of apples is greater than 5, the number of pears is less than 8, and the number of oranges is exactly 2 times the number of apples, print “Hello”.

  • Create a temporary variable containing the result of the expression. Check it in an if statement.

  • Create a temporary variable for each subcondition. Calculate the overall condition using these subconditions.

  • Declare the temporary subcondition variables in a block so that they are not visible outside the block. Use the outer condition outside the block (the block exists to initialize the overall condition).

    You may also turn this block into a function.

  • Refactor the 3 variables holding fruit quantities into a single structure (FruitCounts) with 3 fields, one for each fruit type. Rewrite the code so this structure is used everywhere.