Using std::for_each() instead of for loop

The standard algorithm for_each() is usually more efficent and less error prone than a classical for loop. And after a while it gets more natural to use.

The major nuisance in using for_each(), at least from my point of view, is that we have often to adapt the function we call in it by mem_fun() or mem_fun_ref(). Luckly, lambda function makes it easier for us.

For this example we are about to use this pretty nonsensical class:

class I43
{
private:
const int value_;
public:
I43(int value) : value_(value) {}

int getValue() const { return value_; }

void doSomething() const
{
std::cout << "Doing something on " << value_ << std::endl;
}
};
std::ostream& operator<<(std::ostream& os, const I43& i)
{
return os << i.getValue();
}

The "put to" overload on ostream makes our new class ready to be used for our simple printing utility function.

As usual, let's create some data to work with:

std::vector<I43> vi;
vi.reserve(10);
for(int i = 0; i < 10; ++i)
vi.push_back(i);
dump(vi);

Notice that we push an integer in the vector but, since the underlying vector type is I43, and the I43 ctor having int ad argument is not explicit, our friendly compiler automatically creates an I43 object for us, and put it in the vector.

If we didn't like having an automatic conversion from int to I43 we would have written the constructor in this way:
explicit I43(int value) : value_(value) {}
But we didn't, so our code works fine.

Now we want to call the doSomething() method for each element in the vector. A common approach to this task results in a for using an iterator as loop variable:

typedef std::vector<I43>::iterator IT;
for(IT it = vi.begin(); it != vi.end(); ++it)
it->doSomething();

Using the standard algorithm for_each() is usually considered a better approach:
std::for_each(vi.begin(), vi.end(), std::mem_fun_ref(&I43::doSomething));
Problem is that we have to use a notation that is a bit clumsy. We specify the address of the member function we want to call, then we adapt it to for_each() using std::mem_fun_ref().

A lambda function makes the code a bit more concise and readable:
std::for_each(vi.begin(), vi.end(), [] (I43 i) { i.doSomething(); });

Item 43 of Effective STL, one of the Effective C++ book series by Scott Meyers, says a lot more about why for_each() is better than a for loop

No comments:

Post a Comment