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.