Originally Posted by
manav
Now for the for_each, it is generalized,
for_each is not a generalization of the built-in for-loop. It cannot be, since it is implemented using a for loop, and it is logically impossible to implement something generic using less generic components.
On one hand, for_each is a generalization of the other algorithms. See, you have all these algorithms in the STL that do things, like std::accumulate(). for_each is a generalization of these. accumulate iterates over a range and "adds" (but the add operation need not be an actual plus) each element to a running total. for_each iterates over a range and does something with each element. Adding them to a running total is a possibility. You can implement accumulate in terms of for_each.
On the other hand, for_each is a specialization of the for loop. It is optimized for iterating exactly once over each element in a range. Especially combined with lambdas (C++0x built-ins or Boost.Lambda), this makes for some syntax reduction. For example, let's print every element in a vector and a list of ints, followed by some string.
Using for loops:
Code:
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " bottles of beer.\n";
}
for(std::list<int>::iterator it = l.begin(); it != l.end(); ++it) {
std::cout << *it << " red balloons.\n";
}
Using for_each:
Code:
std::for_each(v.begin(), v.end(), std::cout << lambda::_1 << " bottles of beer.\n");
std::for_each(l.begin(), l.end(), std::cout << lambda::_1 << " red balloons.\n");
Of course, it's even nicer if you get yet more specialized, using Boost's Range and (candidate) RangeEx libraries:
Code:
rex::for_each(v, std::cout << lambda::_1 << " bottles of beer.\n");
rex::for_each(l, std::cout << lambda::_1 << " red balloons.\n");
(On a side note, std::copy to an ostream_iterator would be yet more specialized. But because the lambda syntax is more concise than creating an ostream_iterator, it wouldn't actually be shorter.)
This is a case of getting more specialized, not of getting more generic. You seem to be under the impression that all we want is getting more generic. That's wrong. What we want is building blocks, that we can stack upon each other. Some building blocks are so useful that we want to use them again and again. Others are useful only for the specific case.
For example, rex::for_each could be implemented like this: (Note: I have no idea how the RangeEx library looks exactly.)
Code:
namespace boost {
namespace range {
template <typename Container> inline
typename Container::iterator begin(Container &c)
{
return c.begin();
}
template <typename Container> inline
typename Container::iterator end(Container &c)
{
return c.end();
}
template <typename InputRange, typename Fn> inline
Fn for_each(Range &r, Fn fn)
{
return std::for_each(begin(r), end(r), fn);
}
}
}
namespace rex = boost::range;
And std::for_each could be (and very likely is) implemented like this:
Code:
namespace std {
template <typename InputIterator, typename Fn> inline
Fn for_each(InputIterator first, InputIterator last, Fn fn)
{
for( ; first != last; ++first) {
fn(*first);
}
return fn;
}
}
Why the weird free functions in the rex part, you may ask? Genericity. Adaptability. Did you know that a std:air<Iterator, Iterator> is a range?
Code:
namespace boost { namespace range {
template <typename InputIterator> inline
InputIterator begin(const std::pair<InputIterator, InputIterator> &p)
{
return p.first;
}
template <typename InputIterator> inline
InputIterator end(const std::pair<InputIterator, InputIterator> &p)
{
return p.end;
}
}}
That's interesting, because that's what std::equal_range (and rex::equal_range) return. Suppose you have a sorted vector<int>: [ 1, 3, 4, 4, 4, 6, 6, 7, 9 ]
And you want to print all occurrences of 4 to stdout. (For whatever deranged reason.) Then you can pass the return value of equal_range directly to copy:
Code:
rex::copy(rex::equal_range(v, 4), std::ostream_iterator<int>(std::cout, " "));
It's all about building blocks.