Control Flow & Loops
Master all C++ control flow constructs including if/else, switch, all loop forms, range-based for, C++17 initializers, and structured bindings.
Control Flow in C++
Control flow statements determine the order in which your program executes. C++ provides a rich set of control flow constructs that go well beyond the basics inherited from C. Modern C++ (C++17 and beyond) adds features likeifwith initializers andconstexpr ifthat make control flow more expressive and safer.
A key principle: minimize the scope of variables. Every variable should be declared as close to its first use as possible, and in the narrowest scope possible. C++17 features likeifwith initializers help you follow this principle.
if/ else a switch
Příkazyifaswitchjsou primárními mechanismy větvení. C++17 přidává klauzule inicializátoru do obou:
#include <iostream>
#include <map>
#include <string>
#include <optional>
std::optional<int> find_value(const std::string& key) {
std::map<std::string, int> data = {{"alpha", 1}, {"beta", 2}, {"gamma", 3}};
if (auto it = data.find(key); it != data.end()) { // C++17 if with initializer
return it->second;
}
return std::nullopt;
}
int main() {
// C++17: if with initializer — the variable's scope is limited to the if/else
if (auto val = find_value("beta"); val.has_value()) {
std::cout << "Found: " << *val << '\n';
} else {
std::cout << "Not found\n";
}
// 'val' is no longer in scope here — cleaner!
// switch with C++17 initializer
enum class Color { Red, Green, Blue };
Color c = Color::Green;
switch (auto name = "unknown"; c) { // C++17 switch with initializer
case Color::Red: name = "red"; std::cout << name << '\n'; break;
case Color::Green: name = "green"; std::cout << name << '\n'; break;
case Color::Blue: name = "blue"; std::cout << name << '\n'; break;
}
// [[fallthrough]] attribute (C++17) — document intentional fallthrough
int priority = 2;
switch (priority) {
case 1:
std::cout << "Critical: ";
[[fallthrough]]; // Tells the compiler this is intentional
case 2:
std::cout << "Important: ";
[[fallthrough]];
case 3:
std::cout << "Handle it\n";
break;
default:
std::cout << "Unknown priority\n";
}
}Stisknutím tlačítka Spustit zkompilujete a spustíte tento příklad.
Smyčky: for, while, do-while a na základě rozsahu for
C++ nabízí více forem smyček. Smyčka na základě rozsahu pro (C++11) je preferována při iteraci přes kontejnery, protože eliminuje chyby typu off-by-one a je jasnější:
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main() {
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
// Traditional for loop — use when you need the index
for (std::size_t i = 0; i < names.size(); ++i) {
std::cout << i << ": " << names[i] << '\n';
}
// Range-based for loop (C++11) — preferred for iteration
for (const auto& name : names) { // const auto& avoids copying
std::cout << name << '\n';
}
// Range-based for with structured bindings (C++17)
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}, {"Charlie", 92}};
for (const auto& [name, score] : scores) { // Destructure the pair
std::cout << name << " scored " << score << '\n';
}
// C++20: range-based for with init-statement
for (auto v = std::vector{1, 2, 3, 4, 5}; auto elem : v) {
std::cout << elem << ' ';
}
std::cout << '\n';
// while loop — use when the number of iterations is unknown
int n = 1;
while (n <= 1024) {
std::cout << n << ' ';
n *= 2;
}
std::cout << '\n';
// do-while — body executes at least once
int input;
do {
std::cout << "Enter a positive number: ";
input = 42; // Simulated input for example
} while (input <= 0);
}Stisknutím tlačítka Spustit zkompilujete a spustíte tento příklad.
Strukturované vazby (C++17)
Strukturované vazby (C++17) umožňují rozložit objekt na jeho základní části. Pracují s poli, strukturami se všemi veřejnými členy,std::pair,std::tuplea jakýmkoli typem, který podporuje protokol strukturovaných vazeb.
Syntaxeauto [a, b] = expr;vytváří pojmenované odkazy na komponentyexpr. Můžete přidat kvalifikátoryconsta&:const auto& [a, b] = expr;vytváří const reference bez kopírování.
Strukturované vazby jsou obzvláště výkonné s iteracístd::map(rozkládajícístd::pair), návraty funkcí, které používajístd::pairnebostd::tuple, a návratovou hodnotou metodyinsertna asociativních kontejnerech.
Použijtebreakk předčasnému ukončení smyčky, když najdete, co potřebujete. Pomocícontinuepřeskočte zbytek aktuální iterace a přejděte na další. Oba usnadňují pochopení smyček, když se používají uvážlivě.
Pokud index nepotřebujete, upřednostněte na základě rozsahu pro před indexem. Eliminuje celé třídy chyb: chyby off-by-one, použití nesprávné proměnné jako indexu a upozornění na porovnání podepsané/nepodepsané.
Pokud index potřebujete, zvažte použitístd::size_tjako typu proměnné smyčky, aby odpovídal návratovému typu.size(), nebo použijtestd::ssize()(C++20), který vrací velikost se znaménkem.
Vyhněte se hluboce vnořeným smyčkám – pokud zjistíte, že vnořujete tři nebo více smyček, extrahujte vnitřní smyčky do funkcí. To zlepšuje čitelnost a testovatelnost.
- Pokud index není potřeba, upřednostněte cykly for založené na rozsahu před cykly založené na indexu
- Chcete-li předejít zbytečným kopiím, použijte const auto& v rozsahu
- Use structured bindings to decompose pairs and tuples in loop variables
- Extract deeply nested loops into named functions for clarity
constexpr if (C++17): Compile-Time Branching
if constexpr evaluates the condition at compile time and discards the branch that is not taken. This is essential in template programming where different branches may not even be valid for all types:
#include <iostream>
#include <type_traits>
#include <string>
template <typename T>
void describe(T value) {
std::cout << "Value: " << value;
if constexpr (std::is_integral_v<T>) {
// This branch only compiles when T is an integer type
std::cout << " (integer, " << (value % 2 == 0 ? "even" : "odd") << ")";
} else if constexpr (std::is_floating_point_v<T>) {
// This branch only compiles when T is a floating-point type
std::cout << " (floating-point)";
} else {
std::cout << " (other type)";
}
std::cout << '\n';
}
int main() {
describe(42); // Value: 42 (integer, even)
describe(3.14); // Value: 3.14 (floating-point)
describe("hello"); // Value: hello (other type)
}Stisknutím tlačítka Spustit zkompilujete a spustíte tento příklad.
- C++17 if/switch with initializers limit variable scope to the statement — use them to keep code clean
- Range-based for loops eliminate off-by-one errors and signed/unsigned comparison issues
- Structured bindings (C++17) decompose pairs, tuples, and structs into named variables
- Use [[fallthrough]] to document intentional switch case fallthrough
- if constexpr (C++17) evaluates conditions at compile time — the discarded branch is not compiled
- Minimalizovat rozsah proměnných: deklarovat proměnné co nejblíže prvnímu použití
Kvíz — Otestujte své znalosti
(15 XP)1. Jaká je výhoda`if`s inicializátorem C++17:`if (auto x = f(); x > 0)`?
2. Co dělá`for (const auto& [key, val] : my_map)`?
3. Co se stane s vyřazenou větví příkazu`if constexpr`?