C++Foundations

C++ Control Flow: Structured Bindings, if-init, switch, Ranges & Modern Iteration (C++23)

TT
TopicTrick Team
C++ Control Flow: Structured Bindings, if-init, switch, Ranges & Modern Iteration (C++23)

C++ Control Flow: Structured Bindings, if-init, switch, Ranges & Modern Iteration (C++23)


Table of Contents


Structured Bindings: Unpacking Data Elegantly

Structured bindings (C++17) decompose composite objects into named variables — eliminating verbose .first/.second access and manual tuple index subscripting:

cpp

Performance note: Structured bindings are zero-cost — they're a syntactic decomposition compiled away completely. The generated assembly is identical to manual .first/.second access.


Structured Bindings with Custom Types

Make your own types work with structured bindings by specializing std::tuple_size, std::tuple_element, and providing a get<N> function:

cpp

if with Initializer (C++17)

Variable scope leak is a common source of bugs: a variable declared before an if statement exists throughout the entire enclosing scope even though it's only used in the if block. C++17 solves this:

cpp

switch with Initializer (C++17)

cpp

Scoped Enums (enum class) and Exhaustiveness

C-style enum values leak their names into the enclosing scope and implicitly convert to integers. enum class fixes both:

cpp

Range-Based For: Deep Dive

Range-based for loops work with any type that has begin() and end() iterators — including custom containers:

cpp

The Spaceship Operator <=> and Three-Way Comparison

Before C++20, providing full ordering for a type required six operators: <, >, <=, >=, ==, !=. The spaceship operator generates all of them from one declaration:

cpp

std::variant and std::visit: Type-Safe Unions

std::variant is a type-safe union — holds exactly one of a set of types at runtime. std::visit dispatches based on the held type:

cpp

C++23 Range-For Improvements

C++23 adds std::views::enumerate and std::views::zip — the Python-style conveniences missing from C++20:

cpp

Frequently Asked Questions

Is range-based for loop always zero-overhead compared to index loops? Yes — the compiler converts for (auto& x : container) into for (auto __it = begin(container), __end = end(container); __it != __end; ++__it). There is no runtime difference vs a manual iterator loop. Common compilers vectorize both equally.

Can I break out of a range-based for early? Yes — break works normally. continue skips to the next iteration. For complex iteration patterns, std::ranges::find_if or algorithms are often cleaner than manual break.

When should I use std::variant instead of inheritance? Use std::variant when: the set of types is closed and known at compile time, you want value semantics (no heap allocation, copyable), and you want exhaustiveness checking at compile time. Use inheritance (polymorphism) when: the type set needs to be open (extended by users), or when virtual dispatch's runtime flexibility outweighs the overhead.


Key Takeaway

Modern C++ control flow features move logic from the "what" to the "why." Structured bindings declare your intent (I want the key and value from this map entry). If-initializers declare lifetime scope (this iterator only matters inside this if). enum class prevents accidental integer conversion. Every feature reduces the gap between what you intend and what the code does.

Read next: C++ Functions: Parameters, References & noexcept →


Part of the C++ Mastery Course — 30 modules from modern C++ basics to expert systems engineering.