13.4. Iterable ADT’s

How can we visit each element in a container and remain ignorant of the underlying container implementation details? For example, given:

array<string, 3> names = {"Alice", "Bob", "Clara"};
std::list<int>   ages  = {27, 3, 1};

What options do we have for operating on each element in names and ages? A traditional for or while loop works for names:

for (unsigned i=0; i < names.size(); ++i) {
  cout << names[i] << '\n';
}

unsigned i = 0;
while(i < names.size()) {
  cout << names[i++] << '\n';
}

However, this code does not compile:

for (unsigned i=0; i < ages.size(); ++i) {
  cout << ages[i] << '\n';
}

unsigned i = 0;
while(i < ages.size()) {
  cout << ages[i++] << '\n';
}

Traditional loops using an int index do not work with containers that do not overload operator[]. Containers in this category include list, set, and map.

We solve this problem by avoiding explicit indexing altogether. The range-based for loop provides a more readable equivalent to the traditional for loop:

for (string s: names) {
  cout << s << '\n';
}

// better: avoids copying
for (const auto& s: names) {
  cout << s << '\n';
}

The same syntax can be used for any STL container:

std::list<int>  ages  = {27, 3, 1};

for (const auto& a: ages) {
  cout << a << '\n';
}

When you need to loop over each element in a collection, the range-for loop has completely abstracted away the idea of moving from one element to the next.

We say these containers are iterable.


More to Explore

You have attempted of activities on this page