SovranCode
Modern C++ Lesson 11
Course contents50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
Modern C++50 available · 0 planned

Get a program running

01 · C++ Introduction: What Is C++?02 · How to Install a C++ Compiler and Set Up Your Editor03 · C++ Hello World: Your First Program Explained04 · How to Compile and Run C++ Programs05 · C++ Debugging: Warnings and Breakpoints

Work with values and input

06 · C++ Variables: Initialization and Scope07 · C++ Data Types: int, double and Type Conversion08 · C++ const, constexpr and auto Explained09 · C++ Operators: Arithmetic, Comparison and Logic10 · C++ User Input: cin, getline and Validation

Control flow and functions

11 · C++ Conditions: if, else if and else12 · C++ Switch Statement: Cases, break and default13 · C++ For Loop: Syntax and Examples14 · C++ While and Do-While Loops: Examples15 · C++ Functions: Parameters and Return Values

Strings and sequences

16 · C++ Strings: std::string with Examples17 · C++ Vector: std::vector with Examples18 · C++ Arrays: std::array and Bounds Checking19 · C++ Range-Based For Loop and Iterators20 · C++ Vector Practice: Build a Data Summary

Model data with classes

21 · C++ Structs and enum class Explained22 · C++ Classes and Objects: Encapsulation23 · C++ Constructors and Member Initialization24 · C++ Const Member Functions and Composition25 · C++ Copying Objects and the Rule of Zero

Lifetime and resource ownership

26 · C++ Object Lifetime, Destructors and RAII27 · C++ Pointers and References: nullptr and Borrowing28 · C++ unique_ptr: Smart Pointers and Ownership29 · C++ Move Semantics: std::move vs Copying30 · C++ string_view and span: Avoid Dangling Views

Containers and algorithms

31 · C++ map vs unordered_map: Lookup and Examples32 · C++ set, queue and stack: When to Use Each33 · C++ Lambda Expressions and STL Algorithms34 · C++ sort and binary_search: Rules and Complexity35 · C++20 Ranges: Views, filter and transform

Errors, files, and interfaces

36 · C++ optional and variant Explained37 · C++ Exceptions: try, catch and Safe Cleanup38 · C++ File Handling: Read and Write Text Files39 · C++ Input Parsing and Data Validation40 · C++ Polymorphism: Virtual Functions and Interfaces

Generic programming and quality

41 · C++ Templates: Function and Class Examples42 · C++20 Concepts and Template Constraints43 · C++ Unit Testing: Normal and Boundary Cases44 · C++ Sanitizers: Find Memory and Undefined Behavior Bugs45 · C++ Performance: Benchmark Before Optimizing

Build and deliver a complete application

46 · C++ Header Files and Translation Units47 · C++ CMake Tutorial: Build an Executable48 · C++ Dependencies and CTest: Automated Tests49 · C++ Multithreading: Threads and Data Races50 · C++ Command-Line Applications: Help and Release Checks
PREVIOUS LESSONC++ User Input: cin, getline and Validation
NEXT LESSONC++ Switch Statement: Cases, break and default
C++ foundations · Lesson 11 About 55 min with practice

C++ Conditions: if, else if and else

Write C++ if, else if and else conditions, trace exclusive branches, use guard clauses, avoid dangling else and assignment mistakes, and test below, at and above each threshold.

Before you begin

Understand Boolean comparisons and validated input. Branching examples run in the compiler-backed editor when available or with your local C++20 compiler.

What you will learn

  • Trace which branch an if statement executes.
  • Distinguish exclusive chains from independent checks.
  • Order validation and threshold decisions correctly.
  • Use braces and early returns to clarify control flow.
  • Test boundaries and verify exactly one classification.

A condition chooses which work happens

An if statement evaluates a condition and executes its controlled statement when that condition is true. An optional else executes when the condition is false. This is control flow: unlike a plain arithmetic expression, it determines which statements are reached. Begin with a written decision contract. Our sample workshop allows ages zero through 120, classifies ages below twelve as child tickets, ages twelve through seventeen as youth tickets, and ages eighteen upward as standard tickets.

Those age cutoffs are fictional teaching rules, not legal or real-world eligibility advice. They give us precise endpoints to test. Invalid input must be rejected before classification, and every accepted value must receive exactly one category. If you cannot state whether a value exactly at twelve is child or youth, your requirement is incomplete before any C++ syntax is written. Good conditions encode a clear rule; they do not discover the policy for you.

The condition in parentheses is contextually converted to bool. Comparisons already produce Boolean results and usually express intent more clearly than testing an unexplained numeric value. An integer zero is false and a nonzero integer is true in this context, but relying on that conversion can hide an assignment mistake. Prefer age == 12 when testing equality and keep modifications outside the decision expression while learning.

A ticket decision first rejects ages below zero or above 120, then checks below twelve for child, below eighteen for youth, and otherwise standard. One accepted path produces one label; validation precedes all categories.
Follow one path. Later conditions are reached only when earlier conditions in the chain are false.
C++ PRACTICE RUNNER

Trace one exclusive branch

age is twelve: the child check is false, the youth check is true, and the standard branch is skipped. Change the value to eleven, seventeen and eighteen before separate builds.

#include <iostream>
int main() {
    const int age{12};
    if (age < 0 || age > 120) {
        std::cout << "Invalid age\n";
        return 1;
    }
    if (age < 12) {
        std::cout << "Child\n";
    } else if (age < 18) {
        std::cout << "Youth\n";
    } else {
        std::cout << "Standard\n";
    }
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Youth

After editing inputs, calculate a new prediction; this reference describes only the starter.

else if is an ordered decision

An else if chain tests the next condition only after the preceding condition was false. Once one branch is taken, later branches in that chain are skipped. In our example, reaching age < 18 already implies age is at least twelve because age < 12 failed. You can repeat that lower bound for emphasis, but understand why the chain makes it unnecessary. The order carries information, so rearranging thresholds can change the result.

Check the most restrictive ascending threshold first for this policy. If age < 18 appears before age < 12, every child age also satisfies the first condition and is labeled youth; the child branch becomes unreachable for valid inputs. That is a logic error despite a successful build. Trace one value from each category through every condition instead of only checking whether the expected output string appears somewhere in the source.

Separate if statements are independent checks. A value can make several of them true, producing several labels or applying several actions. That is appropriate for independent facts, such as a report that notes both membership and an accessibility request. It is wrong when the contract requires exactly one ticket category. Choose the structure from whether decisions are exclusive, not from a preference for shorter syntax. Multiple successful outputs can be a bug, not extra helpful information.

C++ PRACTICE RUNNER

Spot a valid program with the wrong classification

This is an intentional logic-error example, not a compile failure. For age ten it prints both categories. Repair by changing the second if into else if, then test twelve and eighteen too.

#include <iostream>
int main() {
    const int age{10};
    if (age < 12) { std::cout << "Child\n"; }
    if (age < 18) { std::cout << "Youth\n"; }
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Child
Youth

After editing inputs, calculate a new prediction; this reference describes only the starter.

Use a guard before the normal path

A guard clause detects a case that cannot proceed and exits the current function early. In main, return 1 ends the program with a failure status for our invalid input. After the guard returns, the remaining code can rely on the permitted age range. This reduces nesting and separates rejection from normal classification. It does not automatically validate every property; the accepted assumptions must match the guard's actual comparisons.

Nested if statements are useful when a second question matters only after a first one succeeds. For example, only an available workshop might need a membership discount decision. Avoid nesting merely because you have not separated independent requirements. Deep indentation can obscure which condition governs a print statement. Write down the path conditions for each action; if the explanation is difficult, named Boolean values or an early rejection often improve the structure.

A guard also protects arithmetic. If the count is zero, report that an average is unavailable and return before dividing. Place the check before the operation rather than printing an error after an unsafe expression has already executed. That connection between validation and control flow is central to reliable C++: code is safe because required preconditions hold when it is reached, not because a later message admits something went wrong.

Braces make ownership of a branch visible

Without braces, an if controls one statement. Indentation may suggest that a second print belongs to the branch, but the compiler does not use indentation to decide that relationship. Use braces consistently for these beginner examples, including single-statement bodies. They make adding a second action less likely to change behavior accidentally and give the reader a visible boundary around each path.

An else attaches to the nearest preceding unmatched if, not to whichever indentation level looks most convincing. This is the dangling-else problem. Add braces around an outer condition before placing an inner condition inside it, and give each else a clear partner. Do not solve it by experimenting with whitespace. The source should communicate the same nesting to a human reader and to the compiler.

An accidental semicolon immediately after if (condition) creates an empty controlled statement. A following block can then run independently, even when the condition is false. Similarly, assigning inside a condition can change the input and test the assigned value rather than compare it. Warnings can reveal suspicious cases; braces, explicit comparisons and targeted tests make the intended behavior clearer. A warning-clean build still needs logic verification.

C++ PRACTICE RUNNER

Make each else partner explicit

The outer decision concerns availability. The inner decision concerns membership. Here availability is false, so neither inner label is reached.

#include <iostream>
int main() {
    const bool available{false};
    const bool member{true};
    if (available) {
        if (member) {
            std::cout << "Member price\n";
        } else {
            std::cout << "Regular price\n";
        }
    } else {
        std::cout << "Unavailable\n";
    }
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Compare with the starter's expected output
Unavailable

After editing inputs, calculate a new prediction; this reference describes only the starter.

A decision table is a test plan

For our ticket rule, test minus one, zero, eleven, twelve, seventeen, eighteen, 120 and 121. The invalid values must terminate with Invalid age; the accepted endpoints must receive exactly one category. The threshold-adjacent values expose off-by-one choices that a middle-of-range example will miss. Record the expected status as well as text, because an invalid-input path should not look like successful completion to a calling script.

Do not use the word covered to mean every input has been tested. These cases target the branches and boundaries of this small policy; they do not prove arbitrary later changes correct. If you add a new condition, identify which existing cases still discriminate it and which new cases are required. If you change < to <=, explicitly revisit the exact threshold. Tests should make a policy change visible rather than silently blessing whatever the program now prints.

When a branch behaves unexpectedly, inspect the actual value, the Boolean result, and the earlier path that allowed this condition to be reached. Check for overlapping independent if statements, reversed threshold order, a semicolon after the condition, or an assignment where equality was intended. Repair the smallest cause and rerun all discriminating cases. The next planned lesson introduces switch for discrete choices; it does not replace ordered interval checks like this age classification.

LOCAL TERMINAL COMMANDS

Build and run the local examples

Save one complete example or lab solution as main.cpp. Choose your installed toolchain; run only after a successful build. Input examples wait for text in your terminal. The browser teaching interpreter does not support these full-language features.

macOS / Clang:
clang++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson

Linux / GCC:
g++ -std=c++20 -Wall -Wextra -Wpedantic main.cpp -o lesson
./lesson

Windows Developer Command Prompt / MSVC:
cl /std:c++20 /EHsc /W4 main.cpp /Fe:lesson.exe
lesson.exe
APPLY THE CONCEPT

Independent lab

Read one whole-line age and classify a fictional workshop ticket. Reject invalid syntax, trailing text, and ages outside zero through 120 before choosing exactly one category.

Acceptance checklist

  • Read and validate a whole line using the preceding input lesson's parser pattern.
  • Reject a word, an empty line, and trailing nonwhitespace.
  • Reject ages below zero or above 120 with exit status one.
  • Use child below twelve, youth below eighteen, otherwise standard.
  • Test eleven/twelve and seventeen/eighteen; print exactly one label.
Need a hint?
  1. Keep parsing failure distinct from age range failure.
  2. Apply the guard before the exclusive chain.
  3. After the child condition fails, the youth branch already has a lower-bound guarantee.
C++ PRACTICE RUNNER

Write and run your lab

Build your own solution before revealing the reference. Use the input field for normal, boundary and invalid cases; this runner executes your program but does not grade it.

#include <iostream>
#include <string>
#include <sstream>
#include <limits>

int main() {
    // Implement the acceptance checklist above.
    return 0;
}

TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Your lab evidence and notes

Record predictions, actual output, commands and the cause of each repair. Code is plain text: this scratchpad does not execute or grade C++.

Loading local notes…

Review your acceptance checklist and quiz explanations before marking this lesson complete. This is self-review, not a grade or certification.

Reveal the explained lab solution
C++ PRACTICE RUNNER

Reference lab solution

Compare the reference with your own reasoning. Running this solution does not grade or complete the lesson.

#include <iostream>
#include <sstream>
#include <string>
int main() {
    std::string line;
    if (!std::getline(std::cin, line)) { std::cout << "Invalid input\n"; return 1; }
    std::istringstream parser{line};
    int age{};
    if (!(parser >> age)) { std::cout << "Invalid input\n"; return 1; }
    parser >> std::ws;
    if (!parser.eof()) { std::cout << "Invalid input\n"; return 1; }
    if (age < 0 || age > 120) { std::cout << "Invalid age\n"; return 1; }
    if (age < 12) { std::cout << "Child\n"; }
    else if (age < 18) { std::cout << "Youth\n"; }
    else { std::cout << "Standard\n"; }
    return 0;
}
TERMINAL OUTPUT
Predict the output, edit the source, then select Run code.

C++20 compiler runner: source and input are sent only when you press Run to this website's execution endpoint, not an external vendor. Local preview requires Docker and gcc:14; production requires a separately isolated execution service. Network is disabled; execution is limited to 3 seconds, compilation to 10 seconds, memory to 384 MB and output to 32 KB. Single-file programs only. Don't enter secrets. Edits reset on refresh; Copy your work to keep it.

Expected output

Standard

Parsing must succeed for the entire line before age participates in the policy. The range guard rejects invalid ages before a category can be printed.

At twelve the child test is false, so youth applies. At eighteen both less-than tests are false, so standard applies.

The else-if chain prints exactly one accepted category. Invalid input and invalid age are separate rejection paths with failure status one.

Review before moving on

Explain the lesson's objectives in your own words, compare your lab evidence with the acceptance checklist, and keep the first failing case alongside your repair. Completing a page is not proof that every C++ feature or input is supported.

  • Trace which branch an if statement executes.
  • Distinguish exclusive chains from independent checks.
  • Order validation and threshold decisions correctly.
  • Use braces and early returns to clarify control flow.
  • Test boundaries and verify exactly one classification.

References and further reading

Original examples and exercises accompany these primary references. Tool-specific diagnostics and support depend on your installed version.

  • C++ draft: if statement
  • C++ draft: return statement
  • C++ Core Guidelines: statements
KNOWLEDGE CHECK

Check your understanding

Answer all five questions, then review the explanations. Answers are not saved or synced; completion is a separate self-review choice.

01How many branches of one if/else-if/else chain execute?
02Why check age < 12 before age < 18 in this policy?
03What does a guard clause do here?
04Which if owns an unbraced dangling else?
05Which pair discriminates the youth-to-standard boundary?
PREVIOUS LESSONC++ User Input: cin, getline and Validation
NEXT LESSONC++ Switch Statement: Cases, break and default
ON THIS PAGELearning objectivesA condition chooses which work happenselse if is an ordered decisionUse a guard before the normal pathBraces make ownership of a branch visibleA decision table is a test planIndependent labReviewKnowledge checkReferences
Course overview