CppRange-Based for Loop

Range-Based for Loop

C++11 introduced the range-based for loop, a simpler and safer syntax for iterating over every element of a container or array without manually tracking an index or an iterator. It reads almost like plain English: "for each element in this collection."
Basic Syntax

CPP
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> numbers = {10, 20, 30, 40};

    for (int n : numbers) {
        cout << n << " ";
    }
    cout << endl;
    // Output: 10 20 30 40

    return 0;
}

Compare this to the traditional index-based loop it replaces:

CPP
// The old way - works, but more room for off-by-one mistakes
for (size_t i = 0; i < numbers.size(); i++) {
    cout << numbers[i] << " ";
}

The range-based version is shorter, cannot go out of bounds, and works identically across arrays, std::vector, std::string, std::map, and any other type that exposes begin()/end().

auto vs auto&: Copies vs References
By default, writing for (auto x : container) copies each element into x. For small types like int this is harmless, but for larger objects (a std::string, a custom class, a std::vector of vectors) that copy is wasted work on every single iteration.

CPP
vector<string> names = {"Alice", "Bob", "Charlotte"};

// Copies each string - wasteful for large strings, and mutations
// to 'name' do NOT affect the vector.
for (auto name : names) {
    name += "!";  // only modifies the local copy
}

// No copy - 'name' is a reference to the actual element.
// Mutations here DO affect the vector.
for (auto& name : names) {
    name += "!";
}
cout << names[0] << endl; // Output: Alice!
Warning
Using plain auto instead of auto& in a range-based for loop silently copies every element. For large or expensive-to-copy types, this can quietly make a loop far slower than it needs to be, and it also means changes inside the loop body never reach the original container.
const auto& for Read-Only Iteration
When you only need to read elements and never modify them, use const auto&. It avoids the copy of auto while also preventing accidental modification, which is the safest and most common form you'll see in idiomatic C++ code.

CPP
vector<string> names = {"Alice", "Bob", "Charlotte"};

for (const auto& name : names) {
    cout << name << " has " << name.size() << " characters" << endl;
    // name += "!";  // would not compile - name is const
}

Form

Copies element?

Can modify original?

Typical use

auto x

Yes

No

Small, cheap-to-copy types (int, char)

auto& x

No

Yes

Need to modify elements in place

const auto& x

No

No

Read-only access, especially to large objects

Iterating a C-Style Array

Range-based for also works directly on fixed-size C-style arrays, because the compiler knows their size at compile time.

CPP
#include <iostream>
using namespace std;

int main() {
    int scores[] = {85, 90, 78, 92};

    for (int score : scores) {
        cout << score << " ";
    }
    cout << endl;
    // Output: 85 90 78 92

    return 0;
}
Note
Range-based for does not work on a raw pointer to an array (it needs to know the size), so it cannot be used on arrays that have decayed into pointers, such as a function parameter declared as int arr[].
Key Points
  • Range-based for (C++11) iterates every element of a container or array without manual indexing.

  • Plain auto copies each element - use auto& to modify elements or avoid copies of large types.

  • const auto& is the idiomatic choice for read-only iteration over non-trivial element types.

  • Works on arrays, std::vector, std::string, std::map, and any type with begin()/end().

  • Cannot be used on a pointer that has decayed from an array - the size must be known.