Team LiB
Previous Section Next Section

14.8. Call Operator and Function Objects

14.8. 调用操作符和函数对象

The function-call operator can be overloaded for objects of class type. Typically, the call operator is overloaded for classes that represent an operation. For example, we could define a struct named absInt that encapsulates the operation of converting a value of type int to its absolute value:

可以为类类型的对象重载函数调用操作符。一般为表示操作的类重载调用操作符。例如,可以定义名为 absInt 的结构,该结构封装将 int 类型的值转换为绝对值的操作:

     struct absInt {
         int operator() (int val) {
             return val < 0 ? -val : val;
         }
     };

This class is simple. It defines a single operation: the function-call operator. That operator takes a single parameter and returns the absolute value of its parameter.

这个类很简单,它定义了一个操作:函数调用操作符,该操作符有一个形参并返回形参的绝对值。

We use the call operator by applying an argument list to an object of the class type, in a way that looks like a function call:

通过为类类型的对象提供一个实参表而使用调用操作符,所用的方式看起来像一个函数调用:

     int i = -42;
     absInt absObj;  // object that defines function call operator
     unsigned int ui = absObj(i);     // calls absInt::operator(int)

Even though absObj is an object and not a function, we can make a "call" on that object. The effect is to run the overloaded call operator defined by the object absObj. That operator takes an int value and returns its absolute value.

尽管 absObj 是一个对象而不是函数,我们仍然可以“调用”该对象,效果是运行由 absObj 对象定义的重载调用操作符,该操作符接受一个 int 并值并返回它的绝对值。

The function-call operator must be declared as a member function. A class may define multiple versions of the call operator, each of which differs as to the number or types of their parameters.

函数调用操作符必须声明为成员函数。一个类可以定义函数调用操作符的多个版本,由形参的数目或类型加以区别。



Objects of class types that define the call operator are often referred to as function objectsthat is, they are objects that act like functions.

定义了调用操作符的类,其对象常称为函数对象,即它们是行为类似函数的对象。

Exercises Section 14.8

Exercise 14.31:

Define a function object to perform an if-then-else operation: The function object should take three parameters. It should test its first parameter and if that test succeeds, it should return its second parameter, otherwise, it should return its third parameter.

定义一个函数对象执行“如果-则-否则”操作:该函数对象应接受三个形参,它应该测试第一个形参,如果测试成功,就返回第二个形参,否则,就返回第三个形参。

Exercise 14.32:

How many operands may an overloaded function-call operator take?

一个重载的函数调用操作符可以接受多少操作数?


14.8.1. Using Function Objects with Library Algorithms

14.8.1. 将函数对象用于标准库算法

Function objects are most often used as arguments to the generic algorithms. As an example, recall the problem we solved in Section 11.2.3 (p. 400). That program analyzed words in a set of stories, counting how many of them were of size six or greater. One part of that solution involved defining a function to determine whether a given string was longer than six characters in length:

函数对象经常用作通用算法的实参。在第 11.2.3 节解决的问题就是这样一个例子。那个程序分析一组故事中的单词,计算有多少个单词长度在 6 字符以上。该解决方案的一个部分包括定义一个函数以确定给定 string 的长度是否大于 6 字符:

     // determine whether a length of a given word is 6 or more
     bool GT6(const string &s)
     {
         return s.size() >= 6;
     }

We used GT6 as an argument to the count_if algorithm to count the number of words for which GT6 returned true:

使用 GT6 作为传给 count_if 算法的实参,以计算使 GT6 返回 true 的单词的数目:

     vector<string>::size_type wc =
                     count_if(words.begin(), words.end(), GT6);

Function Objects Can Be More Flexible than Functions
函数对象可以比函数更灵活

There was a serious problem with our implementation: It hardwired the number six into the definition of the GT6 function. The count_if algorithm runs a function that takes a single parameter and returns a bool. Ideally, we'd pass both the string and the size we wanted to test. In that way, we could use the same code to count strings of differing sizes.

我们的实现有个严重问题:它将 6 这个数字固化在 GT6 函数的定义中。count_if 算法运行只用一个形参且返回 bool 的函数。理想情况下,应传递 string 和我们想要的长度进行测试。通过该方式,可以使用同一代码对不同长度的字符串进行计数。

We could gain the flexibility we want by defining GT6 as a class with a function-call member. We'll name this class GT_cls to distinguish it from the function:

通过将 GT6 定义为带函数调用成员类,可以获得所需的灵活性。将这个类命名为 GT_cls 以区别于函数:

     // determine whether a length of a given word is longer than a stored bound
     class GT_cls {
     public:
         GT_cls(size_t val = 0): bound(val) { }
         bool operator()(const string &s)
                            { return s.size() >= bound; }
     private:
         std::string::size_type bound;
     };

This class has a constructor that takes an integral value and remembers that value in its member named bound. If no value is provided, the constructor sets bound to zero. The class also defines the call operator, which takes a string and returns a bool. That operator compares the length of its string argument to the value stored in its data member bound.

这个类有一个构造函数,该构造函数接受一个整型值并用名为 bound 的成员记住那个值。如果没有提供值,构造函数将 bound 置 0。该类也定义了调用操作符,接受一个 string 参数并返回一个 bool。调用操作符将 string 实参的长度与数据成员 bound 中存储的值相比较。

Using a GT_cls Function Object
使用 GT_cls 函数对象

We can do the same count as before but this time we'll use an object of type GT_cls rather than the GT6 function:

可以像前面一样进行计数,但这一次使用 GT_cls 类型的对象而不是 GT6 函数:

     cout << count_if(words.begin(), words.end(), GT_cls(6))
          << " words 6 characters or longer" << endl;

This call to count_if passes a temporary object of type GT_cls rather than the function named GT6. We initialize that temporary using the value 6, which the GT_cls constructor stores in its bound member. Now, each time count_if calls its function parameter, it uses the call operator from GT_cls. That call operator tests the size of its string argument against the value in bound.

这个 count_if 调用传递一个 GT_cls 类型的临时对象而不再是名为 GT6 的函数。用整型值 6 来初始化那个临时对象,构造函数将这个值存储在 bound 成员中。现在,count_if 每次调用它的函数形参时,它都使用 GT_cls 的调用操作符,该调用操作符根据 bound 的值测试其 string 实参的长度。

Using the function object, we can easily revise our program to test against another value. We need to change only the argument to the constructor for the object we pass to count_if. For example, we could count the number of words of length five or greater by revising our program as follows:

使用函数对象,容易修改程序以根据其他值进行测试,只需为传给 count_if 的对象改变构造函数实参即可。例如,这样修改程序,就可以计算长度在 5 个字符以上的单词数:

     cout << count_if(words.begin(), words.end(), GT_cls(5))
          << " words 5 characters or longer" << endl;

More usefully, we could count the number of words with lengths greater than one through ten:

更为有用的是,还可以计算长度在 1 到 10 个字符的单词数:

     for (size_t i = 0; i != 11; ++i)
         cout << count_if(words.begin(), words.end(), GT(i))
              << " words " << i
              << " characters or longer" << endl;

To write this program using a functioninstead of a function objectwould require that we write ten different functions, each of which would test against a different value.

如果使用函数代替函数对象来编写这个程序,可能需要编写 10 个不同的函数,每个函数测试一个不同的值。

Exercises Section 14.8.1

Exercise 14.33:

Using the library algorithms and the GT_cls class, write a program to find the first element in a sequence that is larger than a specified value.

使用标准库算法和 GT_cls 类,编写一个程序查找序列中第一个比指定值大的元素。

Exercise 14.34:

Write a function-object class similar to GT_cls but that tests whether two values are equal. Use that object and the library algorithms to write a program to replace all instances of a given value in a sequence.

编写类似于 GT_cls 的函数对象类,但测试两个值是否相等。使用该对象和标准库算法编写程序,替换序列中给定值的所有实例。

Exercise 14.35:

Write a class similar to GT_cls, but that tests whether the length of a given string matches its bound. Use that object to rewrite the program in Section 11.2.3 (p. 400) to report how many words in the input are of sizes 1 through 10 inclusive.

编写类似于 GT_cls 的类,但测试给定的长度是否与其边界相匹配。使用该对象重写 第 11.2.3 节中的程序,以便报告输入中有多少单词的长度在 1 到 10 之间(含 1 和 10)。

Exercise 14.36:

Revise the previous program to report the count of words that are sizes 1 through 9 and 10 or more.

修改前面程序以报告长度在 1 到 9 之间以及 10 以上的单词的数目。


14.8.2. Library-Defined Function Objects

14.8.2. 标准库定义的函数对象

The standard library defines a set of arithmetic, relational, and logical function-object classes, which are listed in Table 14.3 on the following page. The library also defines a set of function adaptors that allow us to specialize or extend the function-object classes defined by the library or those that we define ourselves. The library function-object types are defined in the functional header.

标准库定义了一组算术、关系与逻辑函数对象类,表 14.3 列出了这些类。标准库还定义了一组函数适配器,使我们能够特化或者扩展标准库所定义的以及自定义的函数对象类。这些标准库函数对象类型是在 functional 头文件中定义的。

Table 14.3. Library Arithmetic Function Objects
表 14.3. 标准库函数对象

Arithmetic Function Objects Types

算术函数对象类型

 

plus<Type>

minus<Type>

multiplies<Type>

divides<Type>

modulus<Type>

negate<Type>

applies +

applies -

applies *

applies /

applies %

applies -

Relational Function Objects Types

关系函数对象类型

 

equal_to<Type>

not_equal_to<Type>

greater<Type>

greater_equal<Type>

less<Type>

less_equal<Type>

applies ==

applies !=

applies >

applies >=

applies <

applies <=

Logical Function Object Types

逻辑函数对象类型

 

logical_and<Type>

logical_or<Type>

logical_not<Type>

applies &&

applies |

applies !


Each Class Represents a Given Operator
每个类表示一个给定操作符

Each of the library function-object classes represents an operatorthat is, each class defines the call operator that applies the named operation. For example, plus is a template type that represents the addition operator. The call operator in the plus template applies + to a pair of operands.

每个标准库函数对象类表示一个操作符,即,每个类都定义了应用命名操作的调用操作符。例如,plus 是表示加法操作符的模板类型。plus 模板中的调用操作符对一对操作数应用 + 运算。

Different function-object classes define call operators that perform different operations. Just as plus defines a call operator that executes the + operator; the modulus class defines a call operator that applies the binary % operator; the equal_to class applies ==; and so on.

不同的函数对象定义了执行不同操作的调用操作符。正如 plus 定义了执行 + 操作符的调用操作符,modulus 类定义了应用二元操作符 % 的调用操作符,equal_to 类应用 ==,等等。

There are two unary function-object classes: unary minus (negate<Type>) and logical NOT (logical_not<Type>). The remaining library function objects are binary function-object classes representing the binary operators. The call operators defined for the binary operators expect two parameters of the given type; the unary function-object types define a call operator that takes a single argument.

有两个一元函数对象类:一元减(negate<Type>))和逻辑非(logical_not<Type>))。其余的标准库函数对象都是表示二元操作符的二元函数对象类。为二元操作符定义的调用操作符需要两个给定类型的形参,而一元函数对象类型定义了接受一个实参的调用操作符。

The Template Type Represents the Operand(s) Type
表示操作数类型的模板类型

Each of the function-object classes is a class template to which we supply a single type. As we know from the sequential containers such as vector, a class template is a class that can be used on a variety of types. The template type for the function-object classes specifies the parameter type for the call operator.

每个函数对象类都是一个类模板,我们需要为该模板提供一个类型。正如从诸如 vector 的顺序容器所了解的,类模板是可以用于不同类型的类。函数对象类的模板类型指定调用操作符的形参类型。

For example, plus<string> applies the string addition operator to string objects; for plus<int> the operands are ints; plus<Sales_item> applies + to Sales_items; and so on:

例如,plus<string>string 加法操作符应用于 string 对象,对于 plus<int>,操作数是 int 值,plus<Sales_item>+ 应用于 Sales_items; 对象,依次类推:

     plus<int> intAdd;         // function object that can add two int values
     negate<int> intNegate;   //  function object that can negate an int value
     // uses intAdd::operator(int, int) to add 10 and 20
     int sum = intAdd(10, 20);          // sum = 30
     // uses intNegate::operator(int) to generate -10 as second parameter
     // to intAdd::operator(int, int)
     sum = intAdd(10, intNegate(10));    // sum = 0

Using a Library Function Object with the Algorithms
在算法中使用标准库函数

Function objects are often used to override the default operator used by an algorithm. For example, by default, sort uses operator< to sort a container in ascending order. To sort the container in descending order, we could pass the function object greater. That class generates a call operator that invokes the greater-than operator of the underlying element type. If svec is a vector<string>

函数对象常用于覆盖算法使用的默认操作符。例如,sort 默认使用 operator< 按升序对容器进行排序。为了按降序对容器进行排序,可以传递函数对象 greater。该类将产生一个调用操作符,调用基础对象的大于操作符。如果 svec 是一个 vector<string> 对象,以下代码

     // passes temporary function object that applies > operator to two strings
     sort(svec.begin(), svec.end(), greater<string>());

sorts the vector in descending order. As usual, we pass a pair of iterators to denote the sequence that should be sorted. The third argument is used to pass a predicate (Section 11.2.3, p. 402) function to use to compare elements. That argument is a temporary of type greater<string>, which is a function object that applies the > operator to two string operands.

将按降序对 vector 进行排序。像通常那样,传递一对迭代器以指明被排序序列。第三个实参用于传递比较元素的谓词(第 11.2.3 节)函数。该实参 greater<string> 类型的临时对象,是一个将 > 操作符应用于两个 string 操作符的函数对象。

14.8.3. Function Adaptors for Function Objects

14.8.3. 函数对象的函数适配器

The standard library provides a set of function adaptors with which to specialize and extend both unary and binary function objects. The function adaptors are divided into the following two categories.

标准库提供了一组函数适配器,用于特化和扩展一元和二元函数对象。函数适配器分为如下两类:

  1. Binders: A binder is a function adaptor that converts a binary function object into a unary function object by binding one of the operands to a given value.

    绑定器,是一种函数适配器,它通过将一个操作数绑定到给定值而将二元函数对象转换为一元函数对象。

  2. Negators: A negator is a function adaptor that reverses the truth value of a predicate function object.

    求反器,是一种函数适配器,它将谓词函数对象的真值求反。

The library defines two binder adaptors: bind1st and bind2nd. Each binder takes a function object and a value. As you might expect, bind1st binds the given value to the first argument of the binary function object, and bind2nd binds the value to the second. For example, to count all the elements within a container that are less than or equal to 10, we would pass count_if the following:

标准库定义了两个绑定器适配器:bind1stbind2nd。每个绑定器接受一个函数对象和一个值。正如你可能想到的,bind1st 将给定值绑定到二元函数对象的第一个实参,bind2nd 将给定值绑定到二元函数对象的第二个实参。例如,为了计算一个容器中所有小于或等于 10 的元素的个数,可以这样给 count_if 传递值:

     count_if(vec.begin(), vec.end(),
              bind2nd(less_equal<int>(), 10));

The third argument to count_if uses the bind2nd function adaptor. That adaptor returns a function object that applies the <= operator using 10 as the right-hand operand. This call to count_if counts the number of elements in the input range that are less than or equal to 10.

传给 count_if 的第三个实参使用 bind2nd 函数适配器,该适配器返回一个函数对象,该对象用 10 作右操作数应用 <= 操作符。这个 count_if 调用计算输入范围中小于或等于 10 的元素的个数。

The library also provides two negators: not1 and not2. Again, as you might expect, not1 reverses the truth value of a unary predicate function object, and not2 reverses the truth value of a binary predicate function object.

标准库还定义了两个求反器:not1not2。你可能已经想到的,not1 将一元函数对象的真值求反,not2 将二元函数对象的真值求反。

To negate our binding of the less_equal function object, we would write

为了对 less_equal 函数对象的绑定求反,可以编写这样的代码:

     count_if(vec.begin(), vec.end(),
             not1(bind2nd(less_equal<int>(), 10)));

Here we first bind the second operand of the less_equal object to 10, effectively transforming that binary operation into a unary operation. We then negate the return from the operation using not1. The effect is that each element will be tested to see if it is <= to 10. Then, the truth value of that result will be negated. In effect, this call counts those elements that are not <= to 10.

这里,首先将 less_equal 对象的第二个操作数绑定到 10,实际上是将该二元操作转换为一元操作。再用 not1 对操作的返回值求反,效果是测试每个元素是否 <=。然后,对结果真值求反。这个 count_if 调用的效果是对不 <= 10 的那些元素进行计数。

Exercises Section 14.8.3

Exercise 14.37:

Using the library function objects and adaptors, define an object to:

使用标准库函数对象和函数适配器,定义一个对象用于:

  1. Find all values that are greater than 1024.

    查找大于 1024 的所有值。

  2. Find all strings that are not equal to pooh.

    查找不等于 pooh 的所有字符串。

  3. Multiply all values by 2.

    将所有值乘以 2。

Exercise 14.38:

In the last call to count_if we used not1 to negate the result from bind2nd of (less_equal<int>(), 10). Why did we use not1 rather than not2.

最后一个 count_if 调用中,用 not1bind2nd 的结果求反。为什么使用 not1 而不用 not2

Exercise 14.39:

Use library function objects in place of GT_cls to find the words of a specified length.

使用标准库函数对象代替 GT_cls 来查找指定长度的单词。


Team LiB
Previous Section Next Section