Iterating Through Containers in C++, with Some Help from Boost's Lambda Library and Friends
- Finding a for-each in C++
- Skipping for-each with a while Loop
- Discovering lambda Functions
- Wrapping it All Up
- Conclusion
for-each
, and has helpful devices called lambda functions. Jeff Cogswell shows you how you can fly through your containers with ease.If you have used some of the newer languages, such as Python or C#, you might be familiar with the for-each construct. It is similar to a for construct, but instead of iterating over a range of numbers, it iterates through the elements in a container.
For example, you might have a collection of phone numbers stored in an array. With a regular for loop, you loop from zero to one fewer than the number of items in the container. (Or should that be from one to the number of items in the collection? That kind of confusion depends on the language and is one source of bugs!)
But with a for-each loop, you simply write a loop that iterates through the collection itself. Although the variable in a for loop in this case is an index you use to access the phone numbers in the collection, the variable in the for-each loop is the actual phone number in the collection at each iteration.
Suppose that your collection consists of these numbers:
110-555-1234
800-123-4567
123-867-5309
888-111-1111
In pseudocode, a for-each construct might look like this:
for-each (number in list) { print number }
Finding a for-each in C++
What a lot of people don't know is that the 1998 ANSI standard for C++ actually contains a for_each algorithm, which exists as a template function in the header file <algorithm>. The only catch is that the algorithm doesn't support a block of code that you execute like the "print number" line in the preceding pseudocode. Instead, you pass to for_each an address of a function that gets called for each member in the collection.
Here's some sample C++ code. In this code, I'm creating a map containing names and phone numbers, and I'm iterating through them with the for_each algorithm.
typedef map<string, string> PhoneList; typedef PhoneList::value_type PhonePair; void eachphone(const PhonePair &item) { cout << item.first << " : " << item.second << endl; } void demoMapAlgo() { PhoneList phones; phones[string("Jeff")] = string("110-555-1234"); phones[string("Angie")] = string("800-123-4567"); phones[string("Dylan")] = string("123-867-5309"); phones[string("Amy")] = string("888-111-1111"); for_each(phones.begin(), phones.end(), eachphone); }
If you call this function, here's the output you'll see:
Amy : 888-111-1111 Angie : 800-123-4567 Dylan : 123-867-5309 Jeff : 110-555-1234
The for_each algorithm in this code calls the eachphone function for each item in the map. Working with maps requires two types: a map and a pair. A map holds instances of a pair. In this example, each pair consists of a name and a phone number. To make my life just a wee bit easier, today I created two typedefs to hold these two types. The map is a PhoneList, and each pair the map holds is a PhonePair. Note, however, to get to the type for PhonePair, I go through the PhoneList type and pull out its value_type type; this type represents the pairs stored in the map.
In the Standard Library (which the map template is part of), each container template has a begin() member and an end() member. The begin() member returns an iterator that dereferences to the first item in the collection. The end() member returns what is technically called a "past-the-end" value, which can be used in comparisons to test whether you've reach the end of the sequence. In the context of the for_each algorithm, you just pass the return value of begin() as the first parameter, and the return value of end() as the second. You then pass the address of a function you want to call for each item, and the algorithm does the dirty work for you. Easy!
Sometimes, however, you might not want to call a function. I know that in this example, I would be a little irritated at the world if I had to write an entire function every time I wanted to do just one thing like printing out a single line of text. Call me grumpy, but I like cleaner code than that.