Range-Based For Loops — C++

Syntax

for (declaration : range_expression) { statement }
// Example:
std::vector<int> v = {1, 2, 3, 4, 5};
for (auto x : v) { std::cout << x << "\n"; }

How It Works — Lowering to Iterators

The compiler transforms for (auto x : range) into:

{
    auto&& __range = range;
    auto __begin = begin(__range);
    auto __end   = end(__range);
    for (; __begin != __end; ++__begin) {
        auto x = *__begin;
        // body
    }
}

Works With

// Custom type example
struct Range {
    int* begin() { return data; }
    int* end()   { return data + size; }
    int data[4] = {10, 20, 30, 40};
    int size = 4;
};
Range r;
for (auto v : r) { /* ... */ }

Value vs Reference vs Const Reference

std::vector<std::string> names = {"Alice", "Bob", "Carol"};

for (auto name : names)         // COPY — safe but expensive
for (auto& name : names)        // REFERENCE — modify allowed, no copy
for (const auto& name : names)  // CONST REF — safe, no copy (preferred for read)

[!TIP] Prefer const auto& for read-only iteration over containers holding non-trivial types - it avoids copies entirely.


Structured Bindings with Range-For (C++17)

std::map<std::string, int> grades = {{"Alice", 95}, {"Bob", 88}};


Modifying Elements

Must use auto&:

std::vector<int> v = {1, 2, 3, 4};
for (auto& x : v) { x *= 2; }  // v is now {2, 4, 6, 8}

Index Tracking Workaround

Range-for has no built-in index. Options:

// Manual counter
int i = 0;
for (const auto& x : v) { std::cout << i << ": " << x << "\n"; ++i; }

// C++23 enumerate (ranges::views::enumerate)
// for (auto [i, x] : std::views::enumerate(v)) { ... }

[!NOTE] C++23 introduces std::views::enumerate which provides index-value pairs directly in a range-for loop.


Range-for Expansion

flowchart TD A[“for (auto x : range)”] –> B[“auto range = range_expr”] B –> C[“auto begin = begin(range)”] C –> D[“auto end = end(range)”] D –> E{“begin != end?”} E –>|”Yes”| F[“auto x = *begin”] F –> G[“Execute body”] G –> H[”++begin”] H –> E E –>|”No”| I[“Loop ends”]

Which Loop Form to Choose

flowchart TD A[“Range-for loop”] –> B{“Need to modify elements?”} B –>|”Yes”| C[“for (auto& x : range)”] B –>|”No”| D{“Elements expensive to copy?”} D –>|”Yes”| E[“for (const auto& x : range)”] D –>|”No”| F[“for (auto x : range)”]


Summary Table

SyntaxUse caseModifies?Notes
for (auto x : v)Small/cheap types, read onlyNoMakes a copy each iteration
for (auto& x : v)Any type, modify elementsYesDirect reference to element
for (const auto& x : v)Any type, read onlyNoMost efficient for read-only
for (auto& [k,v] : map)Key-value containersYesC++17 structured binding
for (const auto& [k,v] : map)Key-value containers, readNoC++17 structured binding