Home > Articles

Imperfect C++: Functors and Ranges

  • Print
  • + Share This
In most cases, functors and ranges cause a minor issue of too many keystrokes, but in some cases, they can cause a serious impediment to maintainability. This chapter will teach you to deal with this problem while it's still a minor one.
This chapter is from the book

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 imperfection—barely even a minor gripe—but 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.

  • + Share This
  • 🔖 Save To Your Account