← C++, End to End

C++, End to End

Control Flow

C++ inherits its control flow almost verbatim from C, then adds range-based for and a couple of quality-of-life rules on top. None of this is exotic, but a few of the defaults are surprising if you've come from a language that made different choices.

if and else

Standard stuff: if (cond) { } optionally followed by else if chains and a final else. The condition just needs to be contextually convertible to bool, which is looser than it sounds: pointers, integers, and anything with an explicit operator bool() all qualify. That looseness is also a classic source of bugs, if (x = 5) compiles fine and assigns 5 to x, then branches on whether 5 is truthy (it is), when you almost certainly meant ==.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

Most compilers warn on this with -Wall (-Wparentheses). Treat that warning as an error in your own projects; there is essentially never a legitimate reason to write if (x = y) on purpose without wrapping it in extra parens to signal intent.

switch

switch compares one value against a set of constant case labels. The part that trips people up coming from other languages: cases fall through by default. Execution continues into the next case unless you break.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

Stacking case labels with no code between them (like 1: through 5: above) is the one place fallthrough is genuinely useful: it lets several labels share one block. Falling through *into* a block that has its own statements is the dangerous kind, and it usually means a missing break. If you ever want intentional fallthrough into a non-empty case, mark it with the [[fallthrough]]; attribute (C++17) so the compiler and the next reader both know it was deliberate rather than a bug.

Loops: while, do-while, for

while (cond) { } checks the condition before each iteration, so the body might run zero times. do { } while (cond); checks after, so the body always runs at least once, useful for things like "read input, then keep reading while it's valid". The classic for (init; cond; step) { } is just a while loop with the bookkeeping baked into the header.

c++ · live, editable, runnableOpen in Compiler Explorer ↗

Range-based for

C++11 added a for that iterates a container or array directly, without manual indexing:

c++ · live, editable, runnableOpen in Compiler Explorer ↗

Default to const auto& when you're only reading, auto& when you need to mutate, and plain auto (by value) only when the elements are cheap to copy or you genuinely want a copy. Copying every element of a large container just to read it is a common, easy-to-miss performance mistake.

break, continue, and goto

break exits the nearest enclosing loop or switch immediately. continue skips to the next iteration's condition check. Both are unremarkable and safe to use freely.

goto still exists. In almost forty years of C++ code most people write, there is essentially one legitimate use left: jumping to a single cleanup label at the end of a function to avoid duplicating cleanup code across multiple early-exit points, and even that use case is mostly obsolete now that RAII (covered later, under memory and resource management) handles cleanup automatically. Outside of that narrow case, if you find yourself reaching for goto, it's almost always a sign the function should be restructured, split into smaller functions, or rewritten with a loop and a flag.

The loop-and-a-half problem

Sometimes the natural loop condition needs to check something that's only known partway through the loop body, classically: read a line, then loop while the read succeeded. A while loop can't express "check after reading, but before doing anything else with what was read" cleanly. Two common patterns:

  1. An infinite loop (while (true)) with an explicit if (...) break; where the natural exit condition becomes known.
  2. Restructuring so the check-then-use both happen inside the loop condition itself, e.g. while (std::getline(input, line)) { ... }, which works because std::getline returns a stream convertible to bool.
Try it yourself: fix the fallthrough bug

This switch statement is supposed to print a grade letter based on a score, but it prints multiple lines for some inputs. Find the missing break and fix it, then verify with a few different score values.

c++ · live, editable, runnableOpen in Compiler Explorer ↗