Skip to content
C++ Better Explained
Go back
C++ Iterators Explained: How to Traverse STL Containers for Beginners
Edit page

C++ Iterators: How to Traverse STL Containers

When you use a range-based for loop like for (auto& x : vec), C++ is quietly using iterators under the hood. Understanding iterators directly gives you more control: you can iterate backwards, skip elements, erase while traversing, or plug into STL algorithms.

Think of an iterator as a smart pointer to a container element — you can dereference it to get the value, and increment it to move forward.


The Basic Iterator Loop

Here’s the explicit iterator syntax for a vector:

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

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

    // Declare an iterator and loop
    for (vector<int>::iterator it = nums.begin(); it != nums.end(); ++it) {
        cout << *it << " ";  // Dereference to get value
    }
    cout << endl;

    return 0;
}

Output: 10 20 30 40 50

The three pieces:


Cleaner with auto

The type vector<int>::iterator is verbose. Use auto to let the compiler infer it:

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

int main() {
    vector<string> fruits = {"apple", "banana", "cherry"};

    for (auto it = fruits.begin(); it != fruits.end(); ++it) {
        cout << *it << endl;
    }

    return 0;
}

This is equivalent to the verbose version but much easier to read.


Modifying Elements Through Iterators

You can modify container elements through an iterator by assigning to *it:

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

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};

    // Double every element
    for (auto it = nums.begin(); it != nums.end(); ++it) {
        *it *= 2;
    }

    for (auto n : nums) {
        cout << n << " ";  // 2 4 6 8 10
    }
    cout << endl;

    return 0;
}

Reverse Iterators: rbegin() and rend()

To traverse a container backwards, use rbegin() (last element) and rend() (before first):

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

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};

    for (auto it = nums.rbegin(); it != nums.rend(); ++it) {
        cout << *it << " ";  // 5 4 3 2 1
    }
    cout << endl;

    return 0;
}

The r versions give you iterators that move backwards when incremented.

If you're looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you're a complete beginner or looking to solidify your understanding. Just $19.

Iterating Over a Map

Maps store pair<key, value> elements. The iterator gives you access to both:

#include <iostream>
#include <map>
using namespace std;

int main() {
    map<string, int> scores = {
        {"Alice", 92},
        {"Bob", 85},
        {"Carol", 97}
    };

    for (auto it = scores.begin(); it != scores.end(); ++it) {
        cout << it->first << ": " << it->second << endl;
    }

    return 0;
}

it->first is the key, it->second is the value. The -> syntax is shorthand for (*it).first.


Iterating Over a Set

Sets work similarly, but every element is its own key:

#include <iostream>
#include <set>
using namespace std;

int main() {
    set<int> s = {5, 3, 1, 4, 2};

    for (auto it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";  // 1 2 3 4 5 (sorted)
    }
    cout << endl;

    return 0;
}

Erasing While Iterating

A common pitfall: erasing from a container while iterating invalidates the current iterator. The safe pattern:

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

int main() {
    vector<int> nums = {1, 2, 3, 4, 5, 6};

    // Remove all even numbers
    auto it = nums.begin();
    while (it != nums.end()) {
        if (*it % 2 == 0) {
            it = nums.erase(it);  // erase returns next valid iterator
        } else {
            ++it;
        }
    }

    for (auto n : nums) {
        cout << n << " ";  // 1 3 5
    }
    cout << endl;

    return 0;
}

erase() returns an iterator to the element that followed the erased one, so you use that as the new it.


Iterators with STL Algorithms

Many STL algorithms (like find, sort, count_if) take iterator pairs:

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

int main() {
    vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6};

    // Find first element equal to 5
    auto it = find(nums.begin(), nums.end(), 5);
    if (it != nums.end()) {
        cout << "Found 5 at index " << distance(nums.begin(), it) << endl;
    }

    // Sort the vector
    sort(nums.begin(), nums.end());

    for (auto n : nums) cout << n << " ";
    cout << endl;

    return 0;
}

The begin()/end() pairs tell algorithms which range of elements to operate on.


Iterator Types Summary

Iterator typeWhat it can do
Input iteratorRead elements, move forward once
Output iteratorWrite elements, move forward once
Forward iteratorRead/write, move forward multiple times
BidirectionalMove forward and backward (list, map)
Random accessJump to any position (vector, deque)

For most beginner work, you’ll use bidirectional or random access iterators without thinking about the distinction.



Take Your C++ Further

If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.

👉 Get the C++ Better Explained Ebook — $19

📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Edit page
Share this post on:

Previous Post
C++ Fibonacci Program: Iterative, Recursive, and Memoized Approaches
Next Post
How to Return Multiple Values from a C++ Function: 5 Practical Methods