# Control Flow and Range-Based Loops — C++

Source: https://www.skillbyai.com/en/cpp/c-flow

> 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.](assets/figures/cpp/section-2-map.svg) — 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.

```cpp
#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.

**Quiz:** Why prefer `enum class` over a plain `enum`?

- [ ] It is faster at run time
- [ ] It allows floating-point values
- [x] 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.
