C++11
iteration
containers
programming
best practices

What's the recommended way of iterating a container in C11?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

In C++11, the recommended way to iterate a container is usually the range-based for loop. It is shorter, less error-prone than manual iterator loops, and makes your intent clearer, especially when combined with const auto& for read-only iteration.

Prefer Range-Based for For Ordinary Traversal

If you simply want to visit each element in order, use the range-based loop:

cpp
1#include <iostream>
2#include <vector>
3
4int main() {
5    std::vector<int> values{1, 2, 3, 4};
6
7    for (const auto& value : values) {
8        std::cout << value << '\n';
9    }
10}

This style has several advantages:

  • less boilerplate
  • no explicit iterator type
  • fewer off-by-one mistakes
  • clearer read-only intent

For most loops, that is enough.

Choose Value, Reference, Or const Reference Carefully

The loop variable matters just as much as the loop syntax.

Use const auto& when you only need to read:

cpp
for (const auto& item : items) {
    process(item);
}

Use auto& when you want to modify elements in place:

cpp
for (auto& item : items) {
    item *= 2;
}

Use plain auto only when copying is intentional or inexpensive:

cpp
for (auto item : items) {
    std::cout << item << '\n';
}

This distinction matters because range-based loops can silently copy expensive objects if you omit the reference.

Use Iterators When You Need More Control

The old iterator form is still the right tool when you need:

  • erasure during iteration
  • access to the iterator itself
  • movement that is not simple linear traversal

Example of safe erasure:

cpp
1#include <vector>
2
3int main() {
4    std::vector<int> values{1, 2, 3, 4, 5};
5
6    for (auto it = values.begin(); it != values.end(); ) {
7        if (*it % 2 == 0) {
8            it = values.erase(it);
9        } else {
10            ++it;
11        }
12    }
13}

Trying to do that with a range-based loop is awkward and often incorrect because erasing invalidates the loop's internal traversal state.

Index Loops Still Have A Place

If you need the numeric position, use an index loop deliberately:

cpp
1#include <iostream>
2#include <vector>
3
4int main() {
5    std::vector<std::string> names{"Ada", "Bjarne", "Linus"};
6
7    for (std::size_t i = 0; i < names.size(); ++i) {
8        std::cout << i << ": " << names[i] << '\n';
9    }
10}

Do not force an index loop when you do not need the index. It adds noise and can be less generic because not all containers support random access.

Algorithms Are Often Better Than Loops

Sometimes the best iteration style is not a loop at all. If your goal is to apply a standard operation, the algorithm library expresses intent better.

cpp
1#include <algorithm>
2#include <iostream>
3#include <vector>
4
5int main() {
6    std::vector<int> values{1, 2, 3, 4};
7
8    std::for_each(values.begin(), values.end(), [](int value) {
9        std::cout << value << '\n';
10    });
11}

In C++11, range-based for is often more readable than std::for_each, but the bigger lesson remains: choose the construct that matches the operation, not the one you happen to remember first.

A Practical Rule Of Thumb

For everyday C++11 code:

  1. Use range-based for for straightforward traversal.
  2. Use const auto& by default for read-only access.
  3. Use auto& when modifying elements.
  4. Use iterators when erasing or when traversal logic is special.
  5. Use indices only when the position matters.

That covers most real container iteration decisions.

Common Pitfalls

The most common mistake is writing for (auto item : container) and accidentally copying every element. With large strings, vectors, or custom objects, that can be an unnecessary performance hit.

Another mistake is using indexing on containers that are not random-access, such as std::list. A range-based loop is more generic and usually more idiomatic there.

People also try to erase elements inside a range-based loop. That often leads to invalidated iterators or subtle bugs. Use an explicit iterator loop when the container may change.

Finally, do not confuse "modern" with "always shortest." Range-based loops are usually best, but the right choice still depends on whether you need mutation, erasure, or index information.

Summary

  • In C++11, range-based for is the default recommendation for normal container iteration.
  • 'const auto& is usually the best read-only loop variable choice.'
  • Use auto& to modify elements and plain auto only when copying is intentional.
  • Fall back to iterators when erasing or when traversal needs more control.
  • Use index-based loops only when the numeric position is actually part of the task.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.