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
Iterator Library at cppreference.com
C++ Concepts: Iterator