iterators


Motivation

Containers store data, but they do not define how that data should be traversed. If you access elements directly using indices or container-specific functions, your code becomes tightly coupled to one particular container type.

Iterators exist to solve this problem. They provide a uniform, container-independent way to refer to elements and move through a sequence. This allows the same code to work with different container types.

Without Iterators

Here, traversal logic is tied directly to the container:


#include <vector>
#include <iostream>

int main() {
    std::vector<int> values = {1, 2, 3, 4};

    for (std::size_t i = 0; i < values.size(); ++i) {
        std::cout << values[i] << " ";
    }
}

This approach works for std::vector, but it cannot be reused for containers like std::list, which do not support indexing.

With Iterators

Iterators decouple traversal from the container’s internal structure:


#include <vector>
#include <list>
#include <iostream>

template <typename>
void print_range(Iterator begin, Iterator end) {
    for (Iterator it = begin; it != end; ++it) {
        std::cout << *it << " ";
    }
}

int main() {
    std::vector<int> vec = {1, 2, 3, 4};
    std::list<int> lst = {5, 6, 7, 8};

    print_range(vec.begin(), vec.end());
    print_range(lst.begin(), lst.end());
}

The same function works for both containers because it relies only on the iterator interface. This is the core motivation for pure iterators: enabling generic, reusable code that operates on sequences rather than concrete container types.