Function objects of STL

Source: Internet
Author: User
Tags modulus

Many Algorithm functions (including algorithm) in STL must provide a parameter called a function object. Why is such a parameter required?

First, these algorithms usually perform some operation on many elements in a container (that is, objects that store a bunch of similar elements, such as list and vector, for example, sort sorts the elements in the container, find_if finds the elements that meet certain conditions in the container, and count_if calculates the total number of elements that meet certain conditions in the container.

For algorithm reusability, the sub-operations should be parameterized, such as the sort sorting principles (sequence and reverse order) and find_if/count_if matching conditions. Function objects are used to describe these suboperations.

Example S-1
//////////////////////////////////////// //////////////////////////////////
1 # include <iostream>
2 # include <iterator>
3 # include <algorithm>
4 # include <functional>
5
6 using namespace STD;
7 void add (Int & RI)
8 {
9 RI + = 5;
10}
11 struct Sub: Public unary_function <int, void>
12 {
13 void operator () (int & ri)
14 {
15 ri-= 5;
16}
17 };
18
19 int main ()
20 {
21 int a [10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
22 copy (a, a + 10, ostream_iterator <int> (cout ,""));
23 cout <endl;
24 for_each (a, a + 10, Add );
25 copy (a, a + 10, ostream_iterator <int> (cout ,""));
26 cout <endl;
27 for_each (a, a + 10, Sub ());
28 copy (a, a + 10, ostream_iterator <int> (cout ,""));
29 cout <endl;
30}
//////////////////////////////////////// //////////////////////////////////

Let's take a look at the prototype of the for_each function:

Template <class InputIterator, class UnaryFunction>
UnaryFunction for_each (InputIterator first, InputIterator last, UnaryFunction f );

The first two parameters are the addresses of the starting element and the ending element respectively. The third parameter is a mona1 function.

In the example S-1, Add and Sub () are both function objects, where Add is a global function, and Add as the for_each parameter in 24 rows.

In Row 27, a function object is instantiated using the Sub structure type and passed to for_each as the parameter. Sub is derived from the base class (actually a structure) unary_function. The definition of unary_function is as follows:

Template <class _ Arg, class _ Result>
Struct unary_function {
Typedef _ Arg argument_type;
Typedef _ Result result_type;
};

A function object can be a common function, a function pointer, and an instance of a class. However, this class must define the parentheses () operator.

Function objects describe an operation. They can also have parameters. Based on the number of parameters of function objects, STL algorithms mainly use three types:

  1. A function object without parameters (generator) is equivalent to "F ()", for example:
    Vector <int> V (10 );
    Generate (V. Begin (), V. End (), RAND );
    "RAND" belongs to generator.
  2. The function object and unary function of a parameter are equivalent to "F (x)". For example, the preceding "add" and "sub ()" also return a mona1 function object. In STL, unary_function is used to define the basis class of a mona1 function.
  3. The function objects and binary functions of two parameters are equivalent to "F (x, y)", for example:
    Struct less_mag: Public binary_function <Double, double, bool>
    {
    Bool operator () (Double X, Double Y) {return FABS (x) <FABS (y );}
    };
    Binary_function defines the binary function base class:
    Template <class _ arg1, class _ arg2, class _ result>
    Struct binary_function {
    Typedef _ arg1 first_argument_type;
    Typedef _ arg2 second_argument_type;
    Typedef _ result result_type;
    };

A function object also has a return value. The return value is a Boolean function object Predicate. It is usually called a mona1 function that returns bool as a Predicate and a binary function that returns bool as a binary Predicate.

Sometimes, you need to convert a binary function object to a one-dimensional function object, or combine several binary predicates into a predicates.

Example S-2
//////////////////////////////////////// //////////////////////////////////
1 # include <iostream>
2 # include <iterator>
3 # include <algorithm>
4 # include <functional>
5
6 using namespace std;
7 int Mul (int ri, int multiplier)
8 {
9 ri * = multiplier;
10 return ri;
11}
12
13 int main ()
14 {
15 int a [10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
16 int B [10];
17 copy (B, B + 10, ostream_iterator <int> (cout ,""));
18 cout <endl;
19 transform (a, a + 10, B, bind2nd (ptr_fun (Mul), 5 ));
20 copy (B, B + 10, ostream_iterator <int> (cout ,""));
21 cout <endl;
22}
//////////////////////////////////////// //////////////////////////////////

For example, in the S-2, 19 rows call ptr_fun to convert a common global function into a binary function object, call the bind2nd function to bind the second parameter multiplier of the binary function object to 5 to become a one-dimensional function object.

The example S-2 is to save a [I] * 5 into B [I], you can use:

Transform (a, a + 10, B, bind2nd (ptr_fun (Mul), 7 ));

Store a [I] * 7 to B [I.

Ptr_fun has two prototypes:
Template <class Arg, class Result>
Pointer_to_unary_function <Arg, Result>
Ptr_fun (Result (* x) (Arg ));

Template <class Arg1, class Arg2, class Result>
Pointer_to_binary_function <Arg1, Arg2, Result>
Ptr_fun (Result (* x) (Arg1, Arg2 ));

The second prototype is used here:
Template <class _ Arg1, class _ Arg2, class _ Result>
Inline pointer_to_binary_function <_ Arg1, _ Arg2, _ Result>
Ptr_fun (_ Result (* _ x) (_ Arg1, _ Arg2 )){
Return pointer_to_binary_function <_ Arg1, _ Arg2, _ Result> (_ x );
}

Therefore, the preceding statement can be written as follows:
Transform (a, a + 10, B, bind2nd (pointer_to_binary_function <int, int, int> (Mul), 7 ));

"Pointer_to_binary_function <_ Arg1, _ Arg2, _ Result>" is a template class. Its constructor uses a binary function pointer as the parameter:
Template <class _ Arg1, class _ Arg2, class _ Result>
Class pointer_to_binary_function: public binary_function <_ Arg1, _ Arg2, _ Result> {
Protected:
_ Result (* _ M_ptr) (_ Arg1, _ Arg2 );
Public:
Pointer_to_binary_function (){}
Explicit pointer_to_binary_function (_ Result (* _ x) (_ Arg1, _ Arg2): _ M_ptr (_ x ){}
_ Result operator () (_ Arg1 _ x, _ Arg2 _ y) const {
Return _ M_ptr (_ x, _ y );
}
};

It is derived from binary_function and has a protection member Variable _ M_ptr to save the binary function pointer and defines the "()" operator, which calls the binary function pointed to by _ M_ptr.

Bind2nd is also an inline function:
Template <class _ Operation, class _ Tp>
Inline binder2nd <_ Operation>
Bind2nd (const _ Operation & _ fn, const _ Tp & _ x)
{
Typedef typename _ Operation: second_argument_type _ Arg2_type;
Return binder2nd <_ Operation> (_ fn, _ Arg2_type (_ x ));
}

The preceding statement can be written as follows:

Transform (a, a + 10, B, binder2nd <pointer_to_binary_function <int, int, int> (pointer_to_binary_function <int, int, int> (Mul), 5 ));

"Binder2nd <_ Operation>" is also a template class. "_ Operation" is replaced by "pointer_to_binary_function <int, int, int>" here. Its constructor has two parameters, the first is the _ Operation class object, and the second is the second parameter type constant of _ Operation:
Template <class _ Operation>
Class binder2nd: public unary_function <typename _ Operation: first_argument_type, typename _ Operation: result_type> {
Protected:
_ Operation op;
Typename _ Operation: second_argument_type value;
Public:
Binder2nd (const _ Operation & _ x, const typename _ Operation: second_argument_type & _ y): op (_ x), value (_ y ){}
Typename _ Operation: result_type operator () (const typename _ Operation: first_argument_type & _ x) const {
Return op (_ x, value );
}
};

It is derived from unary_function, and also has a protection member variable op to save the _ Operation object passed in during instantiation, and has a protection member variable value to save the second parameter value to be bound, the "()" operator is also defined, in which the "()" operator of the binary function object op is called.

From "transform (A, A + 10, B, bind2nd (ptr_fun (Mul), 5);" to "transform (A, A + 10, B, binder2nd <pointer_to_binary_function <int, Int, int> (pointer_to_binary_function <int, Int, int> (Mul), 5 ));", although syntactically complicated (in fact no one can write c ++ code like this), it makes clear the ins and outs of function objects.

It fully utilizes the template features of c ++ and also relies on other keywords, such as typedef and typename.

Template classes such as pointer_to_binary_function and binder2nd are special. They modify other function objects or assemble and generate a new function object. For example, pointer_to_binary_function converts the general global function Mul into a binary function object. binder2nd assembles the binary function object instantiated by pointer_to_binary_function and a constant 5 to generate a one-dimensional function object. Such a template class is called function Object Adapter in STL ).

STL contains many predefined function objects, including:

  1. Arithmetic Operations: Plus, minus, multiplies, divides, modulus, and negate, for example:

    Assert (v2.size ()> = v1.size () & v3.size ()> = v1.size ());
    Transform (v1.begin (), v1.end (), v2.begin (), v3.begin (), modulus <int> ());

    Store V1 [I] % V2 [I] to V3 [I].

     

  2. Comparison operation: performance_to, not_0000_to, greater, less, greater_equal, and less_equal, for example:

    List <int>: iterator first_nonnegative = find_if (L. Begin (), L. End (), bind2nd (greater_equal <int> (), 0 ));

    Find the first element greater than or equal to 0 in L.

     

  3. Logical operation: logical_and, logical_or, and logical_not, for example:

    Char STR [maxlen];
    Const char * wptr = find_if (STR, STR + maxlen, compose2 (logical_or <bool> (), bind2nd (defaults _to <char> (),''), bind2nd (equal_to <char> (), '/N ')));

    Find the first space or the end of the line in Str.

    The above three types are template classes. To generate a function object, it must be instantiated. At the same time, you must note that:

    • What type of function objects are required by the algorithm? Is it a mona1 function or a binary function.
    • Whether the element types in the input container are compatible with the function object parameters, and whether the elements in the output container are compatible with the function object return values.

     

  4. Modify and assemble existing function objects to generate new function objects: unary_compose, binary_compose, unary_negate, binary_negate, binder1st, binder2nd, Hangzhou, Hangzhou, mem_fun_t, Hangzhou, mem_funt T and Hangzhou. This type of function object is called the function Object Adapter. Because of its poor writing, it is generally not instantiated directly, and there are corresponding auxiliary functions (usually inline) for simplified calling.
Function object Template Class Name Auxiliary Function Name Description
Unary_compose Compose1 Unary_compose (const adaptableunaryfunction1 & F,
Const adaptableunaryfunction2 & G );

Generate a one-dimensional function object. The operation is "F (g (x)". The STL of vc6/7 is not defined.

Binary_compose Compose2 Binary_compose (const adaptablebinaryfunction & F,
Const AdaptableUnaryFunction1 & g1,
Const AdaptableUnaryFunction1 & g2 );

Generate a one-dimensional function object. The operation is F (G1 (x), G2 (x). The STL of vc6/7 is not defined.

Unary_negate Not1 Unary_negate (const AdaptablePredicate & pred );

Non-mona1 predicates.

Binary_negate Not2 Binary_negate (const AdaptableBinaryPredicate & pred );

The binary predicates take non.

Binder1st Bind1st Binder1st (const AdaptableBinaryFunction & F,
AdaptableBinaryFunction: first_argument_type c );

Bind the first parameter of the binary function object to C to generate a one-dimensional function object.

Binder2nd Bind2nd Binder2nd (const AdaptableBinaryFunction & F,
AdaptableBinaryFunction: second_argument_type c );

Bind the second parameter of the binary function object to C to generate a one-dimensional function object.

Pointer_to_unary_function Ptr_fun Converts a common global function or a global function pointer (with only one or two parameters) to a function object.

Ptr_fun has two prototypes: The pointer_to_unary_function object and the pointer_to_binary_function object.

Pointer_to_binary_function
Mem_fun_t Mem_fun Converts a member function of a class (with only 0 or 1 parameter, because the first parameter of the member function of the class has been defined as the this pointer) to a function object.

Mem_fun has two prototypes: The mem_fun_t object (a one-dimensional function object) and the mem_funt t object (a binary function object ).

Mem_fun1_t
Mem_fun_ref_t Mem_fun_ref Similar to mem_fun, the differences are as follows:

Struct t {
Print (){}
};

List <t *> lstt;
For_each (lstt. Begin (), lstt. End (), mem_fun (& T: print ));

List <t> lstt;
For_each (lstt. Begin (), lstt. End (), mem_fun_ref (& T: print ));

Mem_fun1_ref_t
     

Example S-3

//////////////////////////////////////// //////////////////////////////////
# Include <string>
# Include <list>
# Include <algorithm>
# Include <iostream>
Using namespace STD;
Class T
{
Public:
T (int i1, int i2, string s) {score1 = i1; score2 = i2; name = s; totalscore = i1 + i2 ;}
Int score1;
Int score2;
Int totalscore;
String name;
Void print ()
{
Cout <name <"/t" <score1 <"/t" <score2 <"/t" <totalscore <endl;
}
};
Int main (int argc, char ** argv)
{
List <T> lstT;
LstT. push_back (T (34,75, "TOM "));
LstT. push_back (T (23, 84, "JIM "));
LstT. push_back (T (43, 63, "JOHN "));
LstT. push_back (T (61,69, "MIKE "));
LstT. push_back (T (21, 90, "ROBIN "));
LstT. push_back (T (59,87, "SALLY "));
LstT. push_back (T (54,72, "LINDA "));
LstT. push_back (T (43, 79, "JAMES "));
For_each (lstT. begin (), lstT. end (), mem_fun_ref (& T: print ));
Cout <endl;

List <T *> lstTa;
LstTa. push_back (new T (34,75, "tom"); // "guw" in vi can chanage a word lowercase.
LstTa. push_back (new T (23,84, "jim "));
LstTa. push_back (new T (43, 63, "john "));
LstTa. push_back (new T (61,69, "mike "));
LstTa. push_back (new T (21, 90, "robin "));
LstTa. push_back (new T (59,87, "sally "));
LstTa. push_back (new T (54,72, "linda "));
LstTa. push_back (new T (43, 79, "james "));
For_each (lstTa. begin (), lstTa. end (), mem_fun (& T: print ));
Cout <endl;
}
//////////////////////////////////////// //////////////////////////////////

 

Reading STL source code is an effective way to improve the experience of C ++. Although its writing style is awkward for many people, reading it carefully will also benefit a lot. Below is a function Object Adapter binder3rd that I learned from STL source code.

STL provides a function (bind1st/bind2nd) to convert a binary function object to a one-dimensional function object. However, sometimes I need to convert a three-element function object to a binary function object, STL does not provide such a function Object Adapter.

First define a type description Trielement function:

Template <class _ Arg1, class _ Arg2, class _ Arg3, class _ Result>
Struct ternary_function {
Typedef _ Arg1 first_argument_type;
Typedef _ Arg2 second_argument_type;
Typedef _ Arg3 third_argument_type;
Typedef _ Result result_type;
};

The binary member function of a class should be a ternary function, so the binary member function should be derived from ternary_function.

Template <class _ Ret, class _ Tp, class _ Arg1, class _ Arg2>
Class const_mem_fun2_ref_t: public ternary_function <_ Tp, _ Arg1, _ Arg2, _ Ret> {
Public:
Explicit const_mem_fun2_ref_t (_ Ret (_ Tp: * _ pf) (_ Arg1, _ Arg2) const): _ M_f (_ pf ){}
_ Ret operator () (const _ Tp & _ r, _ Arg1 _ x, _ Arg2 _ y) const {return (_ r. * _ M_f) (_ x ,__ y );}
Private:
_ Ret (_ Tp: * _ M_f) (_ Arg1, _ Arg2) const;
};

The purpose of binder3rd is to convert a ternary function object to a binary function object. Therefore, binder3rd must be derived from binary_function.

Template <class _ Operation>
Class binder3rd: public binary_function <typename _ Operation: first_argument_type, typename _ Operation: second_argument_type, typename _ Operation: result_type> {
Protected:
_ Operation op;
Typename _ Operation: third_argument_type value;
Public:
Binder3rd (const _ operation & _ x, const typename _ operation: third_argument_type & _ y)
: Op (_ x), value (_ y ){}
Typename _ operation: result_type operator () (const typename _ operation: first_argument_type & _ x, const typename _ operation: second_argument_type & _ y) const {return OP (_ x, _ y, value );}
};

The complete code is as follows:

# Include <string>
# Include <list>
# Include <vector>
# Include <stack>
# Include <queue>
# Include <map>
# Include <set>
# Include <algorithm>
# Include <iostream>
# Include <sstream>
# Include <cstdio>
# Include <cctype>
# Include <cmath>
Using namespace STD;

Template <class _ arg1, class _ arg2, class _ arg3, class _ result>
Struct ternary_function {
Typedef _ Arg1 first_argument_type;
Typedef _ Arg2 second_argument_type;
Typedef _ Arg3 third_argument_type;
Typedef _ Result result_type;
};

Template <class _ Ret, class _ Tp, class _ Arg1, class _ Arg2>
Class const_mem_fun2_ref_t: public ternary_function <_ Tp, _ Arg1, _ Arg2, _ Ret> {
Public:
Explicit const_mem_fun2_ref_t (_ Ret (_ Tp: * _ pf) (_ Arg1, _ Arg2) const): _ M_f (_ pf ){}
_ Ret operator () (const _ Tp & _ r, _ Arg1 _ x, _ Arg2 _ y) const {return (_ r. * _ M_f) (_ x ,__ y );}
Private:
_ Ret (_ Tp: * _ M_f) (_ Arg1, _ Arg2) const;
};

Template <class _ Operation>
Class binder3rd: public binary_function <typename _ Operation: first_argument_type, typename _ Operation: second_argument_type,
Typename _ Operation: result_type> {
Protected:
_ Operation op;
Typename _ Operation: third_argument_type value;
Public:
Binder3rd (const _ Operation & _ x, const typename _ Operation: third_argument_type & _ y)
: Op (_ x), value (_ y ){}
Typename _ Operation: result_type operator () (const typename _ Operation: first_argument_type & _ x, const typename _ Operation: second_argument_type & _ y) const {return op (_ x, _ y, value );}
};

Class T
{
Public:
T (int i1, int i2, string s) {score1 = i1; score2 = i2; name = s; totalscore = i1 + i2 ;}
Int score1;
Int score2;
Int totalscore;
String name;
Bool CompareBy (T aT, int type) const
{
Switch (type)
{
Case 0: // score1
Return (score1> aT. score1 );
Break;
Case 1: // score2
Return (score2> aT. score2 );
Break;
Case 2: // totalscore
Return (totalscore> at. totalscore );
Break;
Default:
Break;
}
Return false;
}
Void print ()
{
Cout <name <"/T" <score1 <"/T" <score2 <"/T" <totalscore <Endl;
}
};

Int main (int argc, char ** argv)
{
List <T> lstT;
LstT. push_back (T (34,75, "TOM "));
LstT. push_back (T (23, 84, "JIM "));
LstT. push_back (T (43, 63, "JOHN "));
LstT. push_back (T (61,69, "MIKE "));
LstT. push_back (T (21, 90, "ROBIN "));
LstT. push_back (T (59,87, "SALLY "));
LstT. push_back (T (54,72, "LINDA "));
LstT. push_back (T (43, 79, "JAMES "));
For_each (lstT. begin (), lstT. end (), mem_fun_ref (& T: print ));
Cout <endl;
LstT. sort (binder3rd <const_mem_fun2_ref_t <bool, T, T, int> (struct <bool, T, T, int> (& T: CompareBy), 0 ));
For_each (lstT. begin (), lstT. end (), mem_fun_ref (& T: print ));
Cout <endl;
LstT. sort (binder3rd <const_mem_fun2_ref_t <bool, T, T, int> (struct <bool, T, T, int> (& T: CompareBy), 1 ));
For_each (lstT. begin (), lstT. end (), mem_fun_ref (& T: print ));
Cout <endl;
LstT. sort (binder3rd <const_mem_fun2_ref_t <bool, T, T, int> (struct <bool, T, T, int> (& T: CompareBy), 2 ));
For_each (lstT. begin (), lstT. end (), mem_fun_ref (& T: print ));
Cout <endl;
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.