Skip to content
Laviyx Learn

Try

C++ course menu

C++ Iterators: begin, end, Iterator Categories and Invalidation

Learn C++ iterators: begin() and end(), dereferencing and advancing, const and reverse iterators, using iterators with algorithms, std::next and std::distance, iterator categories, erasing while iterating, and iterator invalidation.

Intermediate10 min readUpdated
On this page

An iterator is an object that points to an element inside a container, and knows how to move to the next one. Iterators are the glue of the STL: every container provides them, and every algorithm uses them. They behave much like pointers: *it gives the element and ++it moves forward.

begin() and end()#

c.begin() points to the first element. c.end() points one past the last element; it is a marker, not an element, and must never be dereferenced:

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {10, 20, 30};

    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << ' ';
    }
    std::cout << '\n';
}
Output
10 20 30

This half-open range [begin, end) makes an empty container simple: begin() == end(), and the loop runs zero times. The range-based for loop is written in terms of exactly this loop.

Changing elements through an iterator#

C++
#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> names = {"asha", "ravi"};

    for (auto it = names.begin(); it != names.end(); ++it) {
        (*it)[0] = static_cast<char>((*it)[0] - 'a' + 'A');   // capitalize
        std::cout << *it << " has " << it->size() << " letters\n";
    }
}
Output
Asha has 4 letters
Ravi has 4 letters

it->size() is shorthand for (*it).size(), just like with pointers.

const and reverse iterators#

FunctionGives
begin() / end()Read-write iterators
cbegin() / cend()Read-only iterators
rbegin() / rend()Iterators that walk backwards
C++
#include <iostream>
#include <string>

int main() {
    std::string word = "stressed";
    for (auto it = word.rbegin(); it != word.rend(); ++it) {
        std::cout << *it;
    }
    std::cout << '\n';

    std::string reversed(word.rbegin(), word.rend());   // build from a range
    std::cout << reversed << '\n';
}
Output
desserts
desserts

Many containers can be constructed from any iterator range, which is a handy way to copy or convert data.

Iterators and algorithms#

Algorithms take a range as two iterators, so you can work on part of a container:

C++
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {9, 4, 7, 1, 8, 2};

    std::sort(v.begin(), v.begin() + 3);   // sort only the first three
    for (int x : v) std::cout << x << ' ';
    std::cout << '\n';

    auto it = std::find(v.begin(), v.end(), 8);
    if (it != v.end()) {
        std::cout << "8 found at index " << (it - v.begin()) << '\n';
    }
}
Output
4 7 9 1 8 2
8 found at index 4

Search algorithms return end() to mean "not found", so always compare the result with end() before using it.

std::next, std::prev and std::distance#

Not every iterator supports + 3 (for example, a std::list iterator can only step one at a time). The functions in <iterator> work with all of them:

C++
#include <iostream>
#include <iterator>
#include <list>

int main() {
    std::list<char> letters = {'a', 'b', 'c', 'd', 'e'};

    auto third = std::next(letters.begin(), 2);
    auto last = std::prev(letters.end());

    std::cout << *third << ' ' << *last << '\n';
    std::cout << std::distance(letters.begin(), last) << '\n';
}
Output
c e
4

Iterator categories#

Containers provide iterators with different abilities, and each algorithm states which it needs:

CategoryCan doExample containers
Forward++, read/write, multiple passesstd::forward_list, unordered containers
Bidirectionalalso --std::list, std::set, std::map
Random accessalso + n, - n, [], <std::vector, std::deque, std::array, std::string
Contiguous (C++20)random access, elements adjacent in memorystd::vector, std::array, std::string

That is why std::sort (which needs random access) does not work on a std::list; a list has its own .sort() method instead.

Erasing while iterating#

erase invalidates the iterator to the erased element, but returns an iterator to the next one. Use that return value, and only increment when you did not erase:

C++
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> stock = {{"pens", 40}, {"books", 0}, {"bags", 5}, {"caps", 0}};

    for (auto it = stock.begin(); it != stock.end(); ) {
        if (it->second == 0) {
            it = stock.erase(it);   // erase returns the next valid iterator
        } else {
            ++it;
        }
    }

    for (const auto& [item, count] : stock) {
        std::cout << item << ' ' << count << '\n';
    }
}
Output
bags 5
pens 40

For vectors and strings, C++20's std::erase_if is simpler; for maps and sets, std::erase_if also works in C++20.

Iterator invalidation#

Some operations make existing iterators (and pointers and references to elements) invalid: using them afterwards is undefined behaviour.

ContainerInvalidated by
std::vector, std::stringAny insertion that reallocates (all iterators); insert/erase (iterators at or after the position)
std::dequeInsertion or erasure in the middle (all); at the ends (varies)
std::list, std::set, std::mapOnly iterators to the erased elements
std::unordered_*Erasure (erased elements); rehashing during insert (all iterators, but not references)

The safest habit: do not keep iterators around across operations that change the container.

Common mistakes#

Practice#

Practice exercise

Given std::vector<int> v = {3, 8, 2, 8, 5, 8};, use std::find in a loop to print the index of every 8.

Show hint

After finding one match at it, search again starting from std::next(it).

Show solution
C++
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

int main() {
    std::vector<int> v = {3, 8, 2, 8, 5, 8};

    auto it = std::find(v.begin(), v.end(), 8);
    while (it != v.end()) {
        std::cout << std::distance(v.begin(), it) << ' ';
        it = std::find(std::next(it), v.end(), 8);
    }
    std::cout << '\n';
}
Output
1 3 5

Key takeaway#

Finished this lesson?