Pages

Waiting for C++0x for each

Among the many useful stuff we are going to have at hand with the new shiny C++0x standard, there is also a new way of looping, the so called "for each" loop. If you use other languages (as Java or Perl, just to make a couple of names) that is no news at all and, actually, the standard C++ for each remembers them closely, expecially the Java "enhanced loop", as they call it.

For what I can see, there is only an issue with the C++0x for each: currently it is not available. At least, if you are not using the GNU compiler.

If you are not in the GNU club, you still have at least a couple of alternatives, non-standard implementation provided by Boost and Microsoft. I won't suggest you to use them, but you could see them in the code you are working with.

Say that we have a vector of integer, like this:
std::vector<int> vi;
vi.reserve(10);

for(int i = 0; i < 10; ++i)
vi.push_back(i);

And we want to print all its elements on one line, using a blank as a delimiter.

A classic way of doing that is looping with a for from zero to the size of the vector:
for(size_t i = 0; i < vi.size(); ++i)
std::cout << i << ' ';
std::cout << std::endl;

There's nothing wrong with this solution, but it is not much expressive of what we are about to do: we want print any single value of the vector. It could be said louder.

If we are working with Visual C++, we can say it using the Microsoft propertary version of for each:
for each(const int& i in vi)
std::cout << i << ' ';
std::cout << std::endl;

In this way we are saying explicitely we are doing our job for each the elements of our vector - but we are saying it in a non-portable way. So, better to avoid it.

A portable way of doing the same is using the BOOST_FOREACH macro:
BOOST_FOREACH(const int& i, vi)
std::cout << i << ' ';
std::cout << std::endl;

The boost libraries are available for a vast number of C++ compilers, so portability shouldn't be an issue. On the other hand, this is not standard, and someone (me included) doesn't like that much using a macro, when an alternative is given.

And a good alternative is provided by the standard algorithm for_each - better if coupled with a lambda function:
std::for_each(vi.begin(), vi.end(), [] (const int& i) {std::cout << i << ' ';} );
std::cout << std::endl;

Admittedly, if you are not acquainted with lambda functions, this piece of code is not exactely so readable as the previous for each versions but, well, it is so cool. And it says exactely what we are going to do. We can read it in this way: for each element in the interval from begin to end of the vector vi, execute the lambda function that takes as input the element currently fetched and output it, followed by a blank, to the console.

But, do we really need to perform a for loop? In this specific case, a natural way of implementing a solution would require us calling the standard algorithm copy, using an ostream_iterator as destination:
std::copy(vi.begin(), vi.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;

Isn't that a beauty?

No comments:

Post a Comment