पाठ 4 / 25

Control Flow and Range-Based Loops

Write conditions, loops and switches in idiomatic modern C++.

Familiar syntax, modern idioms

C++ control flow will look familiar from C and Java: if/else, switch, while, do-while, for, break and continue. Modern additions make it safer. The range-based for loop (for (const auto& x : container)) iterates over any container or range without index bugs. if with an initialiser (C++17) scopes a variable to the if statement: if (auto it = m.find(key); it != m.end()). if constexpr chooses a branch at compile time inside templates. In a switch, cases fall through unless you break; mark intentional fall-through with [[fallthrough]], and switch over enum class values for type safety. Prefer enum class (scoped enumerations, C++11) to plain enum, because its values do not leak into the surrounding scope and do not convert silently to integers. Use the classic index loop only when you genuinely need the index, and then use std::size_t (or C++20 std::ssize) to avoid signed/unsigned comparison warnings.

Loops over ranges

A range-based loop visits each element in turn without manual index management.

A row of boxes with a moving pointer arrow above stepping from the first to the last box.
Figure 2.1 — Iterating over a container with a range-based for loop.

Modern control-flow idioms

Range-for, if-with-initialiser and a switch over an enum class.

#include <map>
#include <print>
#include <string>
#include <vector>

enum class Grade { A, B, C, Fail };

Grade grade_for(int marks) {
    if (marks >= 85) return Grade::A;
    if (marks >= 70) return Grade::B;
    if (marks >= 40) return Grade::C;
    return Grade::Fail;
}

int main() {
    std::vector<int> marks{91, 64, 38, 77};
    for (const auto m : marks) {
        switch (grade_for(m)) {
            case Grade::A:    std::print("{}: A\n", m); break;
            case Grade::B:    std::print("{}: B\n", m); break;
            case Grade::C:    std::print("{}: C\n", m); break;
            case Grade::Fail: std::print("{}: fail\n", m); break;
        }
    }

    std::map<std::string, int> stock{{"pen", 12}, {"pad", 0}};
    if (auto it = stock.find("pen"); it != stock.end() && it->second > 0) {
        std::print("pens in stock: {}\n", it->second);
    }
}

Signed versus unsigned comparisons

for (int i = 0; i < v.size(); ++i) compares a signed int with an unsigned size, which compilers warn about and which misbehaves for negative values. Prefer range-for, or use std::size_t indices.

त्वरित जाँच: Why prefer `enum class` over a plain `enum`?

  • It is faster at run time
  • It allows floating-point values
  • Its enumerators are scoped and do not implicitly convert to integers
  • It is required for switch statements
Answer

Its enumerators are scoped and do not implicitly convert to integers — Scoped enums avoid name clashes and accidental integer conversions.