Unformatted text preview:

Chapter 13: FunctorsChapter 13: Functors_________________________________________________________________________________________________________Consider a simple task. Suppose you have a vector<string> and you'd like to count the number of strings that have length less than five. You stumble upon the STL count_if algorithm, which accepts a range of iterators and a predicate function, then returns the number of elements in the range for which the function returns true. For example, you could use count_if as follows to count the number of even in-tegers in a vector: bool IsEven(int val) { return val % 2 == 0; } vector<int> myVector = /* ... */ int numEvens = count_if(myVector.begin(), myVector.end(), IsEven);In our case, since we want to count the number of strings with length less than five, we could write a func -tion like this one: bool LengthIsLessThanFive(const string& str) { return str.length() < 5; }And then call count_if(myVector.begin(), myVector.end(), LengthIsLessThanFive) to get the number of short strings in the vector. Similarly, if we want to count the number of strings with length less than ten, we could write a LengthIsLessThanTen function like this one: bool LengthIsLessThanTen(const string& str) { return str.length() < 10; }and then call count_if(myVector.begin(), myVector.end(), LengthIsLessThanTen). In general, if we know in advance what length we want to compare the string lengths against, we can write a function that returns whether a particular string's length is less than that value, then pass it into count_if to get our result. This approach is legal C++, but is not particularly elegant. Every time we want to compare the string length against a particular value, we have to write an entirely new function to perform the compar-ison. Good programming practice suggests that we should instead just write one function that looks like this: bool LengthIsLessThan(const string& str, size_t length) { return str.length() < length; }This more generic function takes in a string and a length, then returns whether the string's length is less than the requested length. This way, we can specify the maximum length as the second parameter rather than writing multiple instances of similar functions.While this new function is more generic than the previous version, unfortunately we can't use it in con-junction with count_if. count_if requires a unary function (a function taking only one argument) as its final parameter, and the new LengthIsLessThan is a binary function. Our new LengthIsLessThan func-tion, while more generic than the original version, is actually less useful in this context. There must be some way to compromise between the two approaches. We need a way to construct a function that takes- 382 - Chapter 13: Functorsin only one parameter (the string to test), but which can be customized to accept an arbitrary maximum length. How can we do this? This problem boils down to a question of data flow. To construct this hybrid function, we need to some-how communicate the upper bound into the function so that it can perform the comparison. So how can we give this data to the function? Recall that a function has access the following information:• Its local variables.• Its parameters.• Global variables.Is there some way that we can store the maximum length of the string in one of these locations? We can't store it in a local variable, since local variables don't persist between function calls and aren't accessible to callers. As mentioned above, we also can't store it in a parameter, since count_if is hardcoded to accept a unary function. That leaves global variables. We could solve this problem using global variables: we would store the maximum length in a global variable, then compare the string parameter length against the glob-al. For example: size_t gMaxLength; // Value to compare against bool LengthIsLessThan(const string& str) { return str.length() < gMaxLength; }This approach works: if our vector<string> is called v, then we can count the number of elements less than some value by writing gMaxLength = /* ... some value ... */ int numShort = count_if(v.begin(), v.end(), LengthIsLessThan);But just because this approach works does not mean that it is optimal. This approach is deeply flawed for several reasons, a handful of which are listed here:• It is error-prone. Before we use LengthIsLessThan, we must take care to set gMaxLength to the maximum desired length. If we forget to do so, then LengthIsLessThan will use the wrong value in the comparison and we will get the wrong answer. Moreover, because there is no formal relationship between the gMaxLength variable and the LengthIsLessThan function, the compiler can't verify that we correctly set gMaxLength before calling LengthIsLessThan, putting an extra burden on the programmer• It is not scalable. If every time we encounter a problem like this one we create a new global vari -able, programs we write will begin to fill up with global variables that are used only in the context of a single function. This leads to namespace pollution, where too many variables are in scope and it is easy to accidentally use one when another is expected.• It uses global variables. Any use of global variables should send a shiver running down your spine. Global variables should be avoided at all costs, and the fact that we're using them here sug-gests that something is wrong with this setup.None of the options we've considered are feasible or attractive. There has to be a better way to solve this, but how?Chapter 13: Functors - 383 -Functors to the RescueThe fundamental issue at heart here is that a unary function does not have access to enough information to answer the question we're asking. Essentially, we want a unary function to act like a binary function without taking an extra parameter. Using only the tools we've seen so far, this simply isn't possible. To solve this problem, we'll turn to a more powerful C++ entity: a functor. A functor (or function object) is an C++ class that acts like a function. Functors can be called using the familiar function call syntax, and can yield values and accept parameters just like regular functions. For example, suppose we create a functor class called MyClass imitating a function accepting an int and returning a double. Then we could “call” an object of


View Full Document

Stanford CS 106L - Functors

Download Functors
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view Functors and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view Functors 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?