Changing a collection via for_each

If our compiler supports the new C++0x (now C++ 2011) "for each" loop, we should probably use it. But currently, when we need to loop on a collection to change its values in some way, a good option is represented by the STL standard algorithm for_each().

for_each() is a non-mutating algorithm, in the sense that it does not change the structure of the collection on which it operates - we can't add of remove items - but it lets us changing item values.

It requires as parameter two input iterators, delimiting the sequence, and a Function object that is meant to use any single item in passed sequence.

To practice on for_each I wrote a simple test fixture (using Google Test) that works on an integer vector:
class ForEachTest : public ::testing::Test
{
protected:
ForEachTest() // 1.
{
vi_.reserve(10);
for(int i = 0; i < 10; ++i)
vi_.push_back(i * i);

vc_.reserve(10);
std::copy(vi_.begin(), vi_.end(), std::back_inserter(vc_)); // 2.
}

void checkVector(const int increase) // 3.
{
for(size_t i = 0; i < vi_.size(); ++i)
EXPECT_EQ(vc_[i] + increase, vi_[i]);
}

std::vector<int> vi_; // used by test cases
private:
std::vector<int> vc_; // copy for comparison
};

1. Since I planned to do a few tests all sharing the same structure, I decided to use a fixture. This class provides the basic setup. The ctor initialize an int vector that is going to be changed by the for_each() usage - a copy of the original vector is kept so that we can perform a test on the result.
2. It could be useful to notice how I copied the original vector using the STL copy() algorithm. A back_inserter() on the destination was required since we actually have to push any new item in it.
3. Utility function used to compare the expected result with the actual one.

A normal free function.

If the job we want to perform on our sequence is trivial (as here: increasing each element by three) we could use just a free function:
void increase3(int& value)
{
value += 3;
}

TEST_F(ForEachTest, IncreaseFunction)
{
std::for_each(vi_.begin(), vi_.end(), increase3);
checkVector(3);
}

Functor.

Often a free function is not enough. For instance, we could be interested in using different increase values, so a functor looks a more suitable solution:
class Increaser
{
private:
int step_;
public:
Increaser(int step) : step_(step) {}
void operator()(int& value) { value += step_; }
};

TEST_F(ForEachTest, IncreaseFunctor)
{
const int step = 5;
std::for_each(vi_.begin(), vi_.end(), Increaser(step));

checkVector(step);
}

Lambda

Sometimes we'd love to have the flexibility offered by functors, but we are not so keen in having a standalone class just for such a limited job. In this case se can use a lambda function:
TEST_F(ForEachTest, IncreaseLambda)
{
const int step = 3;
std::for_each(vi_.begin(), vi_.end(), [step] (int& i) { i += step; });

checkVector(step);
}

Lambda function is a C++0x feature, but if your compiler does not support it yet, you could use the boost implementation instead.

No comments:

Post a Comment