Control Flow

Switch Statements

Master C++ switch statements — cases, break, default, fall-through, enum switches, C++17 switch initialiser, and when to prefer switch over if-else chains.

Switch Statements

When you need to compare one variable against many possible values, a switch statement is cleaner and often faster than a long if-else if chain. C++ switch works with integer types, char, and enum — not floating-point or std::string (directly).


Basic Switch

cpp
#include <iostream>
using namespace std;

int main() {
    int day = 3;

    switch (day) {
        case 1:
            cout << "Monday\n";
            break;
        case 2:
            cout << "Tuesday\n";
            break;
        case 3:
            cout << "Wednesday\n";
            break;
        case 4:
            cout << "Thursday\n";
            break;
        case 5:
            cout << "Friday\n";
            break;
        case 6:
            cout << "Saturday\n";
            break;
        case 7:
            cout << "Sunday\n";
            break;
        default:
            cout << "Invalid day\n";
    }
    // Output: Wednesday

    return 0;
}

Warning: Each case must end with a break statement. Without break, execution falls through to the next case. This is the most common switch mistake.


The break and default

cpp
#include <iostream>
using namespace std;

int main() {
    int choice = 5;

    switch (choice) {
        case 1:
            cout << "One\n";
            break;
        case 2:
            cout << "Two\n";
            break;
        default:
            // default runs if no case matches
            cout << "Unknown choice: " << choice << "\n";
            break;  // Optional in the last case, but add for consistency
    }
    // Output: Unknown choice: 5

    return 0;
}

The default case can appear anywhere in the switch but is conventionally placed last.


Intentional Fall-Through

Sometimes you want fall-through to handle multiple cases identically:

cpp
#include <iostream>
using namespace std;

int main() {
    int month = 4;   // April

    // Grouped cases — share the same code block
    switch (month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            cout << "31 days\n";
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            cout << "30 days\n";
            break;
        case 2:
            cout << "28 or 29 days\n";
            break;
        default:
            cout << "Invalid month\n";
    }
    // Output: 30 days

    return 0;
}

In C++17 you can annotate intentional fall-through with [[fallthrough]] to silence compiler warnings:

cpp
switch (x) {
    case 1:
        doSomething();
        [[fallthrough]];   // Intentional — suppresses warning
    case 2:
        doSomethingElse();
        break;
}

Switch with char

cpp
#include <iostream>
using namespace std;

int main() {
    char grade = 'B';

    switch (grade) {
        case 'A':
            cout << "Excellent!\n";
            break;
        case 'B':
            cout << "Good job!\n";
            break;
        case 'C':
            cout << "Satisfactory.\n";
            break;
        case 'D':
            cout << "Needs improvement.\n";
            break;
        case 'F':
            cout << "Failing.\n";
            break;
        default:
            cout << "Invalid grade.\n";
    }
    // Output: Good job!

    return 0;
}

Switch with Enum

Combining switch with enum is idiomatic C++ — the compiler warns if you miss a case:

cpp
#include <iostream>
using namespace std;

enum class Direction { NORTH, SOUTH, EAST, WEST };

string describe(Direction d) {
    switch (d) {
        case Direction::NORTH: return "Moving north";
        case Direction::SOUTH: return "Moving south";
        case Direction::EAST:  return "Moving east";
        case Direction::WEST:  return "Moving west";
        // No default — compiler warns if a new Direction is added and unhandled
    }
    return "Unknown";
}

int main() {
    cout << describe(Direction::NORTH) << "\n";  // Moving north
    cout << describe(Direction::EAST)  << "\n";  // Moving east
    return 0;
}

Switch with Initialiser (C++17)

Like if, C++17 allows an initialiser in the switch:

cpp
#include <iostream>
#include <map>
using namespace std;

int main() {
    map<string, int> commands = {
        {"start", 1}, {"stop", 2}, {"pause", 3}
    };

    string input = "start";

    // Initialise 'it' scoped to the switch block
    switch (auto it = commands.find(input); (it != commands.end()) ? it->second : 0) {
        case 1:
            cout << "Starting...\n";
            break;
        case 2:
            cout << "Stopping...\n";
            break;
        case 3:
            cout << "Pausing...\n";
            break;
        default:
            cout << "Unknown command.\n";
    }
    // Output: Starting...

    return 0;
}

Switch vs if-else: When to Use Which

SituationUse
Integer / char / enum with many valuesswitch
Range-based comparisons (> 10, < 20)if-else
Floating-point comparisonsif-else
String comparisonsif-else (or map lookup)
Two or three conditionsEither (if-else simpler)
Complex Boolean logicif-else

Strings in Switch — The Workaround

C++ switch does not support strings directly. Use an unordered_map for O(1) dispatch:

cpp
#include <iostream>
#include <unordered_map>
#include <functional>
#include <string>
using namespace std;

int main() {
    unordered_map<string, function<void()>> handlers = {
        {"help",   []{ cout << "Showing help...\n"; }},
        {"quit",   []{ cout << "Quitting...\n";     }},
        {"status", []{ cout << "System OK\n";       }},
    };

    string command = "status";

    if (auto it = handlers.find(command); it != handlers.end()) {
        it->second();   // Call the handler
    } else {
        cout << "Unknown command: " << command << "\n";
    }
    // Output: System OK

    return 0;
}

Practical Example: Simple Calculator

cpp
#include <iostream>
#include <cmath>
using namespace std;

int main() {
    double a, b;
    char op;

    cout << "Enter: number operator number (e.g., 10 + 5): ";
    cin >> a >> op >> b;

    double result = 0.0;
    bool   valid  = true;

    switch (op) {
        case '+':
            result = a + b;
            break;
        case '-':
            result = a - b;
            break;
        case '*':
        case 'x':
        case 'X':
            result = a * b;
            break;
        case '/':
            if (b == 0) {
                cout << "Error: division by zero\n";
                valid = false;
            } else {
                result = a / b;
            }
            break;
        case '%':
            if (b == 0) {
                cout << "Error: modulo by zero\n";
                valid = false;
            } else {
                result = fmod(a, b);
            }
            break;
        case '^':
            result = pow(a, b);
            break;
        default:
            cout << "Error: unknown operator '" << op << "'\n";
            valid = false;
    }

    if (valid) {
        cout << a << " " << op << " " << b << " = " << result << "\n";
    }

    return 0;
}
// Example run:
// Enter: number operator number: 10 / 4
// 10 / 4 = 2.5
//
// Enter: number operator number: 2 ^ 8
// 2 ^ 8 = 256

Summary

  • switch dispatches to a case based on an integer/char/enum value — cleaner than long if-else if chains
  • Every case must end with break — without it, execution falls through to the next case
  • Group cases with empty case bodies to share a code block (intentional fall-through)
  • Use [[fallthrough]] in C++17 to document intentional fall-through and suppress warnings
  • default runs if no case matches — always include it to handle unexpected values
  • switch does not work with double or std::string directly — use if-else or a map
  • Prefer switch with enum class — the compiler warns you when a new enum value is unhandled

Next, we'll learn about for loops — how to repeat operations a fixed number of times and iterate over collections.