[C++] Function object adapter and its underlying principle

Function Object Adapter

bind1st bind2nd

The function object adapter is used to extend the function of the function object and provide a method for passing in additional parameters.

For example, I now have a unary predicate:

class Pred {
    
    
   public:
    bool operator()(int a) {
    
    
        // ...
    }
};

Now call an algorithm, the algorithm will use this unary predicate.

algorithm(..., ..., Pred());

But at the same time, we hope that this predicate can implement an additional function, and this function depends on a temporarily passed in parameter.

So the realization of the predicate was changed:

bool operator()(int a, string b){
    
    
    // ...
}

But this new parameter bcannot be algorithm(..., ..., Pred());passed in during the call .

The adapter will be used at this time.

The basic steps:

Let the class of this predicate inherit a class template binary_function<{参数1类型, 参数2类型, 返回值类型}>, and at the same time constmodify the predicate as a constant function (actually a method that overrides the parent class).

Note that unary predicate inheritanceunary_functionBinary predicate inheritancebinary_function

class Pred: public binary_function<int, string, bool>{
    
    
   public:
    bool operator()(int a, string b) const {
    
    
        // ...
    }
};

Then, when calling the algorithm,

Through a function bind2nd, bind the second parameter to the adapter and pass in this parameter at the same time.

algorithm(..., ..., bind2nd(Pred(), "param");

In addition, we can also use functions to bind1stbind the first parameter to the adapter. At this time, the additional parameters will be passed in the position of the first parameter, and the algorithm will use the second parameter.

bind1st can not only expand unary predicates, but also bind binary predicates and turn them into unary predicates

For example, the following code means a predicate where x> 5:

bind2nd(greater<int>(), 1);

Low-level implementation

As for the function object adapter, if you don't understand its implementation principle, it is often difficult to remember the steps at once.

For example, why do you want to inherit, why is the list of template types <{参数1类型, 参数2类型, 返回值类型}>,

For another example, bind1stwhat work did you do?

Since this semester has been working on things in the web direction, it is inevitable to write js, and writing js will inevitably contact the chain scope.

Although this thing is powerful and flexible, it is also difficult to truly understand and apply. Although I stepped on the pits everywhere, it also trained me the corresponding ability.

Stop talking nonsense, and enter the topic.

When I saw it bind2nd(Pred(), "param"), I immediately thought of packaging. When writing js, it is inevitable to wrap some things up and expand functions.

Reading the implementation code in the library, I found that it probably did these things, which is similar to what I thought:

Return a closure, put the function object and extra parameters in the outer scope, and pass in extra parameters when the algorithm is called.

Described with js as follows:

function bind2nd(fun, extraParam){
    
    
    return function(param){
    
    
        return fun(param, extraParam);
    }
}

In C++, it returns an instance of a class. This instance is constructed with a function object and additional parameters, and then () is rewritten in it to wrap our function object.

In fact, it's already very clear here, but since then I have been thinking about why there is an inheritance step and why inheritance.

In fact, js is the end of writing here, but C++ is a statically typed language. In js, you can pass parameters and return casually. What undefined, null, etc., must be clearly defined in C++.

So in fact, this inheritance is dealing with parameter types and return value types.

After analyzing the implementation code again, I found that he did these tasks:

The class we want to inherit binary_functionis a class template, which is instantiated after inheritance bind2nd(Pred(), "param").

At this time, the parent class is usingspecified by the alias, and the parameters and return value types are recorded. These recorded values bind2ndare used during packaging and used as () rewriting.

Two classes, one for type recording and one for call packaging, just work with the adapter to complete the work.

Probably write it by hand:

This is probably the implementation of the wrapper class:

tmelate<class funType>
class bind2nd {
    
    
   public:
    using p1Type = funType::p1Type;
    using p2Type = funType::p2Type;
    using resultType = funType::resultType;
    binary_function(funType& fun, p2Type extraParam): fun(fun), extraParam(extraParam) {
    
    };
    resultType operator()(const p1Type& arg) const {
    
    
      return fun(arg, value);
    }
   protected:
    funType fun;
    p2Type extraParam; // the right operand
};

This is probably the implementation of the class we want to inherit:

template<class arg1, class arg2, class result>
class binary_function {
    
    
   public:
    using p1Type = typename arg1;
    using p2Type = typename arg2;
    using resultType = typename result; 
};

Guess you like

Origin blog.csdn.net/qq_16181837/article/details/106738651