Conditions: if, else, switch
In the previous lesson, bool appeared — the result of a comparison. But by itself, bool does nothing except store true/false. Conditional statements are what use that result to decide which code to run: if, else, and switch.
01What you will learn
- How
ifuses aboolto execute a block of code conditionally - Chains of
else if/elsefor multiple options switch— an alternative for comparing one value against multiple options- The trap of a forgotten
breakinswitch - When to use
ifand when to useswitch
02Core idea: a fork in the road
Everything that if, else, and switch do is a fork: the program reaches a certain point and must choose which path to take next, depending on some condition. Until now, the code in this course executed strictly top to bottom, statement by statement, with no alternatives — like a straight road with no turns. Conditional statements are the first way to turn off that straight line: the same program, with different variable values, will take different paths and execute different code. if/else is a fork into two (or more) directions based on any condition; switch is the same principle, but for the special case where the fork is built around one specific value.
03if — conditional execution
The simplest form of a fork is "execute or don't execute," with no alternative. That is exactly what if does: it takes a bool expression in parentheses, and if it is true, it executes the { } block that follows. If it is false, the entire block is skipped as if it never existed, and the program continues with the next statement after it.
int hp = 0;
if (hp == 0)
{
std::cout << "Game over\n";
}
hp == 0 is exactly the comparison expression from the previous lesson — it is the condition. The { } block after if is the same compound statement as the body of main() (lesson three) — it is just now executed not always, but conditionally.
04else and else if
else adds an alternative branch — code that runs when the if condition turned out to be false. The easiest way to see this is step by step — from a simple if/else with two branches to a full chain over several conditions.
if (hp <= 0)
{
std::cout << "Game over\n";
}
else
{
std::cout << "You're fine\n";
}
Exactly two outcomes: either the condition hp <= 0 is true and the first branch runs, or it is false and else runs. There is no third option — else here literally catches "everything else."
if (hp <= 0)
{
std::cout << "Game over\n";
}
else if (hp < 30)
{
std::cout << "Low health!\n";
}
else
{
std::cout << "You're fine\n";
}
else if is literally an else that immediately contains a new if: "if the first condition is not true, then check this one." Conditions are checked in order from top to bottom, and exactly one branch runs — the one whose first encountered condition turned out to be true (or the final else, if no condition matched). As soon as one branch runs, the remaining checks in that chain are skipped — even if the next condition would also have been true. The chain can be continued as long as you like — else if can be repeated, the final else is not mandatory, but without it some values may remain completely unhandled.
05switch — comparing one value against options
"Rank 1 — gold, rank 2 — silver, rank 3 — bronze," written with else if, looks like this: if (rank == 1) ... else if (rank == 2) ... else if (rank == 3) ... — the word rank is repeated three times in a row, even though the same thing is always being compared. For this special case (one value, several specific options), C++ has a more compact notation — switch. Again, step by step:
int rank = 2;
switch (rank)
{
case 1:
std::cout << "Gold\n";
break;
default:
std::cout << "No medal\n";
break;
}
switch (rank) — which value we are comparing; case 1: — what we are comparing against; default: — what to do if no case matched (the analog of a final else). Right now rank == 2, no case matches, so default runs and prints "No medal".
int rank = 2;
switch (rank)
{
case 1:
std::cout << "Gold\n";
break;
case 2:
std::cout << "Silver\n";
break;
case 3:
std::cout << "Bronze\n";
break;
default:
std::cout << "No medal\n";
break;
}
Now rank == 2 matches case 2:, std::cout << "Silver\n"; runs, then break; stops execution of the entire switch — the remaining case 3 and default are not even checked. break; at the end of every branch is not a decorative element, but a mandatory part.
int rank = 1;
switch (rank)
{
case 1:
std::cout << "Gold\n";
// break; ← removed on purpose
case 2:
std::cout << "Silver\n";
break;
case 3:
std::cout << "Bronze\n";
break;
default:
std::cout << "No medal\n";
break;
}
// output when rank == 1:
// Gold
// Silver ← unexpected, and this is not a typo in the example
Without break; after case 1:, execution does not stop at its own branch — it falls through into the next one (case 2:) entirely, even though rank is not equal to 2. This is called fallthrough, and it is discussed separately in the "Common mistakes" section below — by default it is almost always a bug, not intent.
06Nested conditions: if and switch inside each other
The character program keeps growing: next to the already familiar hp appears difficulty — a difficulty level that affects what is displayed to the player. The body of an if/else is a { } block, and inside a block there can be any statement, including another if or an entire switch. There is nothing special here — it is the same nesting principle as with ordinary { } from the "else and else if" section, only now the block contains not just std::cout, but an entire conditional construct.
int hp = 60;
int difficulty = 2;
if (hp <= 0)
{
std::cout << "Game over\n";
}
else
{
switch (difficulty)
{
case 1:
std::cout << "Easy mode - alive\n";
break;
case 2:
std::cout << "Normal mode - alive\n";
break;
default:
std::cout << "Hard mode - alive\n";
break;
}
}
The outer if answers one question ("is the character alive"), while the switch inside its else branch answers another ("what difficulty"), but only for the case where the character is indeed alive. There is no need to check difficulty when hp <= 0 — nesting allows you to avoid spending a separate check on that.
The same technique works in the opposite direction — if inside if: for example, first check hp <= 0, and inside that branch, another separate if (hasRevivePotion) to decide whether the game continues or it is a full game over. The rule is the same: the more levels of nesting, the harder the code is to read, so in practice it rarely makes sense to nest deeper than two or three levels — if there are more conditions, it is usually better to extract part of the logic into a separate function (more on that in one of the upcoming lessons).
07if or switch — a table
if / else if | switch | |
|---|---|---|
| Condition | any bool expression (ranges, combinations via &&/||) | comparison of one value against specific constants |
| When more convenient | ranges (hp < 30), complex conditions | many specific options of one value (rank, item type, command code) |
| Must not forget | curly braces when there are multiple statements in a branch | break; at the end of every case |
| Catch-all branch | else — optional, but without it some values may remain unhandled | default: — the same, optional by syntax |
| Nesting | any statement, including another if or switch, can be the body of a branch | the same — inside a case there can be an entire if/else |
Example for rank | if (rank == 1) ... else if (rank == 2) ... else if (rank == 3) ... | switch (rank) { case 1: ... case 2: ... case 3: ... } |
On the same example with rank, you can see where the gain from switch comes from: the word rank is repeated three times in the if variant (once per comparison), but only once in switch — at the very beginning.
08Worse → better: missing curly braces
The task is to print two lines when hp <= 0: "Game over" and "Restarting...". The indentation in the code looks identical in all three versions — but it is not indentation that decides what belongs to the if.
int hp = 50;
if (hp <= 0)
std::cout << "Game over\n";
std::cout << "Restarting...\n";
With hp = 50, the condition hp <= 0 is false — it would seem both lines should be skipped:
if without { } applies only to the single following statement — the second std::cout always runs, regardless of the condition, even though the indentation visually suggests otherwise.
int hp = 50;
if (hp <= 0)
{
std::cout << "Game over\n";
std::cout << "Restarting...\n";
}
Now both lines are part of the block, and with hp = 50 neither of them runs: there is no output at all, as intended.
int hp = 50;
if (hp <= 0)
{
std::cout << "Game over\n";
std::cout << "Restarting...\n";
}
else
{
std::cout << "Still alive\n";
}
The added else is not required to fix the bug — the braces fixed it. But else makes the code more robust in a different way: now both outcomes (hp ran out / hp still there) are written explicitly, and when reading the code there is no question of "what happens if the condition is false?" — the answer is immediately visible.
09Try changing it
- In the example from the "else and else if" section, change
hpto 50 — theelsebranch ("You're fine") will run. - In the example from the "switch" section, remove
break;aftercase 1:— withrank = 1the program will print both "Gold" and "Silver" in a row (fallthrough). - Add another
case 4:with new text to the switch — the syntax is the same as the others.
10Live debugging: common mistakes
As in the previous lesson, none of these mistakes will stop compilation — all three only show up at runtime.
int rank = 1;
switch (rank)
{
case 1:
std::cout << "Gold\n";
// break; ← forgotten
case 2:
std::cout << "Silver\n";
break;
}
Sometimes fallthrough is used intentionally (several cases in a row for the same code), but by default it is almost always a bug. Many compilers warn about this with a separate warning (-Wimplicit-fallthrough in g++/Clang).
The full breakdown with an example output is in the "Worse → better" section above. The rule of this course: curly braces for if/else — always, even for a single statement inside.
= instead of == inside ifif (hp = 0) // assignment instead of comparison — the same mistake as with operators
Here it is especially insidious: the result of the assignment (0) is converted to bool as false, so the if branch never runs, and hp is silently set to zero along the way. The compiler usually warns about this (warning), but does not always stop compilation.
11Check yourself
Three questions: predicting execution through a chain of conditions, diagnosing fallthrough, and choosing between if and switch.
hp = 25?if (hp <= 0) { std::cout << "Game over"; }
else if (hp < 30) { std::cout << "Low health!"; }
else { std::cout << "You're fine"; }Show explanation
It will print "Low health!". Conditions are checked in order: hp <= 0 is false (25 is not ≤ 0), so hp < 30 is checked — it is true (25 < 30), that branch runs, and else is skipped entirely without even being checked.
rank = 1, this program prints both "Gold" and "Silver". How do you fix it?switch (rank) {
case 1: std::cout << "Gold\n";
case 2: std::cout << "Silver\n"; break;
}Show explanation
After case 1: there is no break; — execution does not stop at its own branch but falls through into the next one (case 2:) entirely, even though rank is not equal to 2. This is fallthrough (§05). The fix is to add break; immediately after std::cout << "Gold\n";.
Show explanation
switch — one value (day number) is compared against specific constants 1..7, exactly the case switch was designed for (§05, §07). For comparison: the "cheap/medium/expensive" task based on price ranges (< 10, < 100) cannot be solved with switch at all — there you need if/else if, because ranges are being compared, not specific values.
12Practice
★★ Based on the value of hp, print "Game over" (hp == 0 or less), "Low health!" (hp less than 30), or "You're fine" — using if / else if / else.
Three branches in a row: if (hp == 0), else if (hp < 30), else — and each of the three must have its own std::cout with the corresponding text.
Solution: if (hp == 0) { std::cout << "Game over\n"; } else if (hp < 30) { std::cout << "Low health!\n"; } else { std::cout << "You're fine\n"; }
★★ Based on the value of rank (1, 2, or 3), print "Gold", "Silver", or "Bronze" using switch, with break in every branch and default for other cases.
Four branches: case 1, case 2, case 3, default — each with its own std::cout and a mandatory break; (except possibly the very last branch).
Solution: switch (rank) { case 1: std::cout << "Gold\n"; break; case 2: std::cout << "Silver\n"; break; case 3: std::cout << "Bronze\n"; break; default: std::cout << "No medal\n"; break; }
★★★ Based on the values of hp and difficulty, print the result using nested conditions, as in the "Nested conditions" section above: if hp <= 0 — print "Game over" and do not check anything further. Otherwise — using switch (difficulty) inside the else branch, print "Easy mode - alive" (1), "Normal mode - alive" (2), or "Hard mode - alive" (for the rest, via default).
The outer if/else answers the question "is the character alive" and must print "Game over" in its if branch — the switch goes entirely inside the else, not next to it.
Inside switch (difficulty) — three branches (case 1, case 2, default), each with its own std::cout and break;, exactly as in the example from the "Nested conditions" section.
Solution: if (hp <= 0) { std::cout << "Game over\n"; } else { switch (difficulty) { case 1: std::cout << "Easy mode - alive\n"; break; case 2: std::cout << "Normal mode - alive\n"; break; default: std::cout << "Hard mode - alive\n"; break; } }
13Terms
| Term | Meaning |
|---|---|
| Branching | executing different code depending on a condition — the general name for what if/else/switch do |
| Condition | an expression of type bool that determines which branch runs |
| Fallthrough | execution falling through from one case branch into the next due to a missing break; |
| Nesting | placing one conditional construct inside the body of another |
14What you can do now
Before this lesson, code always executed the same way, top to bottom. Now the program can choose a path:
- You can write an
if/else if/elsechain for several mutually exclusive options and predict exactly which branch will run for a given value. - You can choose between
ifandswitchfor a specific task — and explain whyswitchis not suitable for ranges. - You can find a missing
break;in someone else'sswitchand explain what exactly the fallthrough will output. - You can nest one conditional construct inside another (
switchinsideelse,ifinsideif) and explain why this avoids duplicating a check. - You can explain why missing curly braces for
ifare not a stylistic trifle, but the source of a real bug.
15Summary
if (condition) { ... }— executes the block only if the condition istrue.else if/else— a chain of alternatives; exactly one branch runs.switch— a compact alternative for comparing one value against several constants; everycasebranch must end withbreak;, otherwise it falls through into the next one.- Curly braces for
if/else— always, even for a single statement. =instead of==inside a condition — not a compilation error, but a logical error (usually with a warning).
Conditions choose a path once per program pass. The next lesson is loops: the same "run or not" choice, but repeating again and again while the condition remains true.
if/else/switch work in Unreal C++ without changes — they are part of the language, not the engine. In Blueprint (Unreal's visual scripting), they correspond to the Branch and Switch nodes — the same logic, only as a graph instead of text:
| C++ | Blueprint node | What it looks like |
|---|---|---|
if (condition) { ... } else { ... } | Branch | one input with a bool condition, two output pins: True and False — execution continues along one of them, like the two branches of if/else |
switch (value) { case ... } | Switch on Int / Switch on Enum | one input with a value, a separate output pin for each option plus a Default pin — the direct analog of case and default: |
Nesting works exactly the same way: the True output of one Branch node can lead into the input of the next Branch or Switch — the visual analog of switch inside else from the "Nested conditions" section above. By the time the course reaches Blueprint integration, this parallel will already be familiar.