Imperfect C++: Functors and Ranges
- 34.1 Syntactic Clutter
- 34.2 for_all() ?
- 34.3 Local Functors
- 34.4 Ranges
- 34.5 Functors and Ranges: Coda
- Footnotes
34.1 Syntactic Clutter
Many of the standard library algorithms operate on ranges, where a range is defined as a pair of iterators [Aust1999]. This abstraction is very powerful, and has been exploited to the degree that much of the STL, and, therefore, much of modern C++, relies upon it.
An example of this might be in a simple program to read in integers into a vector:
std::copy( std::istream_iterator<int>(f) , std::istream_iterator<int>() , std::back_inserter(v2)); , std::back_inserter(v2));
In this case, the second argument is a default-constructed iterator that acts as an indicator for the end of range. The two iterators are not connected in a physical sense; the implementation of istream_iterator is such that a default-constructed instance may be interpreted as the logic end point of the range.
Many times we use algorithms over a range of values acquired from a container or containerlike, object as in:
struct dump_string { void operator ()(std::string const &) const; }; std::vector<std::string> > strings = . . .; std::for_each(strings.begin(), strings.end(), dump_string());
As such, it can become tedious, since we replicate the same calls to begin() and end() time and time again. This is miles away from being an imperfectionbarely even a minor gripebut there are circumstances in which it can be a pain. Consider the case when using (pseudo)containers such as glob_sequence (see section 20.6.3) that are only created in order to elicit their range. Let's imagine we want to determine how many Imperfect C++ header files are larger than 1024 bytes, because we want to really immerse ourselves in the magic:
struct is_large : public std::unary_function<char const *, bool> { bool operator ()(char const *file) const { . . . // Return true if "file" > 1024 bytes } }; glob_sequence gs("/usr/include/", "impcpp*"); size_t n = std::count_if(gs.begin(), gs.end(), is_large());
gs is not needed for any other purpose, and its presence otherwise serves only to pollute the local namespace.
An analogous situation occurs when we want to enumerate two or more ranges in the same scope. We can end up introducing several variables for the different begin and end conditions of each range into the current scope, as we saw in section 17.3.2, or using the double-scoping trick from section 17.3.1.