14.4. Iterable Types

How can we visit each element in a container without depending on the container's storage details? For example, an array stores elements contiguously, while a list stores elements in separate nodes. Both can still be visited in order.

14.4.1. Positional indexing

An indexed loop works when the container provides positional access through operator[]. An array is one example:

 1#include <array>
 2#include <cstddef>
 3#include <iostream>
 4#include <string>
 5
 6int main() {
 7  const std::array<std::string, 3> names = {"Alice", "Bob", "Clara"};
 8
 9  for (std::size_t i = 0; i < names.size(); ++i) {
10    std::cout << names[i] << '\n';
11  }
12}

The loop above depends on two operations: size() and operator[]. std::list does not provide positional access through either operator[] or at(). A loop that assumes an integer position therefore cannot be adapted to a list by changing only the container type.

std::set also has no positional indexing. std::map is different: it provides operator[] for lookup by key, not for locating an element by position. For example, records[42] asks for the value associated with key 42; it does not mean the forty-third element.

14.4.2. Range-based for loops

The range-based for loop avoids explicit indexing. The same loop syntax works for standard containers with begin() and end(), including both arrays and lists:

 1#include <array>
 2#include <iostream>
 3#include <list>
 4#include <string>
 5
 6int main() {
 7  const std::array<std::string, 3> names = {"Alice", "Bob", "Clara"};
 8  const std::list<int> ages = {27, 3, 1};
 9
10  std::cout << "names:";
11  for (const auto& name : names) {
12    std::cout << ' ' << name;
13  }
14  std::cout << '\n';
15
16  std::cout << "ages:";
17  for (const auto& age : ages) {
18    std::cout << ' ' << age;
19  }
20  std::cout << '\n';
21}

The declaration const auto& binds a reference to each existing element, so the loop does not copy the element. The const prevents the loop body from changing the container's elements. Use auto& when modification is intended, or auto when an independent copy is useful.

The range-based loop hides the iterator syntax, but it still relies on the same operations: obtain a beginning position, compare it with the end position, dereference the current position, and advance to the next one. We say that containers supporting this interface are iterable.


More to Explore