function and function sub-
In the use of STL, we often need to customize the comparison function. This article will show you how to accomplish this type of function and provide reliable and efficient recommendations for use.
1. Mem_fun, Ptr_fun, Mem_fun_ref
Mem_fun, Ptr_fun, mem_fun_ref the main task is to cover up an inherent grammatical inconsistency in the C + + language.
Calling a function, C + + provides three ways.
f(x)// 语法1:非成员函数的调用。x.f// 语法2:成员函数的调用。p->f()// 语法3:指针调用成员函数。
For Syntax 1:
#include <iostream> #include <vector> using Span class= "Hljs-keyword" >namespace STD ; class Widget {public :}; void test (const widget& one) {cout << " test fine! " << Endl;} int Main () { Vector <widget> VW; For_each (Vw.begin (), Vw.end (), Ptr_fun (test)); //here is not the problem with Ptr_fun. return 0 ;}
For Syntax 2: The above wording is no longer appropriate, the following text gives a corresponding explanation. The correct procedure is as follows, calling Mem_fun_ref.
#include <iostream>#include <vector>usingnamespacestd;class Widget {public: void test() { cout"test fine!" << endl; }};int main() { vector<Widget> vw; for_each(vw.begin(), vw.end(), mem_fun_ref(&Widget::test)); return0;}
For Syntax 3:
#include <iostream>#include <vector>usingnamespacestd;class Widget {public: void test() { cout"test fine!" << endl; }};int main() { vector<Widget*> vw; for_each(vw.begin(), vw.end(), mem_fun(&Widget::test)); return0;}
These three different cases of invocation notation. So what exactly is the reason?
From the implementation of the following for_each, we can see that the implementation of For_each is based on the use of Syntax 1 .
template<classclassfirstlast, UnaryFunction f){ forfirstlast; ++first) { f(*first); } return f;}
This is a common practice in STL, where a function or function object is always used as a syntax for non-member functions when called . So the direct use of Syntax 2 and syntax 3 can not be compiled is very obvious. and Mem_fun and mem_fun_ref to convert them into the form of the corresponding Syntax 1.
Mem_fun's statement is this:
CC>mem_fun(R(C::*pmf)());
Mem_fun_t is a function object adapter that is a function subclass that has a pointer to the function object and provides a operator () function that invokes the member function on the object passed in by argument in operator ().
Similarly, Mem_fun_ref did the same thing.
So, to put it simply, each time a member function is passed to an STL component, it needs to be used.
2. If a class is a function child, you should make it available for mating
In the STL 4 standard function connectors (NOT1, Not2, bind1st and bind2nd) require some special type definitions (Argument_type, First_argument_type, Second_argument_, respectively) Type, return_type). The function objects that are provided with these type definitions are called function objects that can be connected.
#include <iostream>#include <vector>using namespace STD;BOOLCheckinti) {returni = =3;}intMain () { vector<int>v_i{1,2,3,4,5,6l,4}; vector<int>:: Iterator iter = find_if (V_i.begin (), V_i.end (), not1 (check));cout<< *iter << Endl;return 0;}
The intent of this code is to find the first element that does not satisfy a check. But it is not possible to find this function by compiling. The reason for this requires a special type definition with NOT1, so a simple adjustment is required to compile.
vector<int>::iterator iter = find_if(v_i.begin(), v_i.end(), not1(ptr_fun(check)));
If you need to write a function subclass, be sure to inherit from the base structure. Operator () inherits Std::binary_function when there is only one parameter, inheriting std::unary_function, with two parameters.
#include <iostream>#include <vector>using namespace STD;classCheck: Publicunary_function<int,BOOL> {Private:intI Public: Check (intt =0) {i = t; }BOOL operator()(Constcheck& orig)Const{returnORIG.I = = i; }};intMain () { vector<int>v_i{1,2,3,4,5,6l,4}; vector<int>:: Iterator iter = find_if (V_i.begin (), V_i.end (), Not1 (check (1)));cout<< *iter << Endl;return 0;}
If you do not inherit from Unary_function at this time
#include <iostream>#include <vector>using namespace STD;structCheck: Publicbinary_function<Const int*,Const int*,BOOL> {BOOL operator()(Const intIConst int* j)Const{return*i < *j; }};intMain () { vector<int*>V_i; V_i.push_back (New int(2)); V_i.push_back (New int(4)); V_i.push_back (New int(3));int* Temp =New int(3); vector<int*>:: Iterator iter = find_if (V_i.begin (), V_i.end (), bind2nd (check (), temp));cout<< **iter << Endl;return 0;}
3. Design function Subclasses following the principle of pass-by-value
Standard library functions for C + + and C all follow one rule, and function pointers are passed by value.
STL function objects are a form of abstraction and modeling of function pointers, so, as is customary, in STL, function objects are passed by value when they are passed back and forth between functions. This can be done by forcing the type to pass it by reference, but this is dangerous. Because some of the STL's connectors and algorithms take efficiency into account when they accept a function object, so that it is a reference type, and if you declare it as a reference in the template argument at this point, the referenced reference is not compiled.
function pointers are passed by value, meaning two things:
-
- Your function object must be small enough, or the cost of replication will be very large.
-
- The function object must be a single state. Because polymorphism is not feasible, there will be peeling problems.
But it is also impractical for a view to disallow polymorphic functions, and the solution is to separate the required data and virtual functions from the function subclasses, place them in a new class, and then include a pointer in the function subclass that points to the object of the new class.
For example, you want to create a function subclass that contains a large amount of data and uses polymorphism:
template <typename T>classvoid> {private: Widget w; int x;public: virtualvoidoperator()(constconst;};
Then you should create a small, single-state class that contains a pointer to another implementation class, and all of the data and virtual functions are placed in which implementation class:
Template<TypeNameT>classBpfc:unary_function<t,void> {Private: Bpfcimpl<t> *pimpl; Public:void operator()(Constt& orig)Const{pimpl->operator() (orig); }};Template<TypeNameT>classBpfcimpl:unary_function<t,void> {Private: Widget W;intXVirtual~bpfcimpl ();Virtual void operator()(Constt& orig)Const;friend classbpfc<t>;};
The last problem here is to handle the BPFC copy constructor with care so that it correctly handles the Bpfcimpl object it points to. The simple approach is to use a smart pointer with reference counting. Shard_ptr.
4. Ensure that the discriminant is "pure function"
First, a few concepts are given.
- discriminant (predicate): is a function with a return value of bool. The comparison functions needed in STL are usually discriminant.
- Pure function: A function that returns a value that relies solely on its arguments. For example, the same parameter is passed in two times, and its return value is the same.
- discriminant classes (predicate Class): is a function subclass whose operator () is a discriminant that returns BOOL. In STL, where the paradigm accepts discriminant, it can accept either a real discriminant or a discriminant class object.
So why make sure the discriminant is a pure function?
As we mentioned earlier, the function object is passed by value, so you should design a function object that can be copied correctly. In addition, for function objects that are used as discriminant, there is another place to pay special attention when they are copied: The STL algorithm that accepts the function child may first create a copy of the function child, and then store it and then use those copies, and some STL algorithm implementations do take advantage of this feature. The direct reaction of this characteristic is that the discriminant function must be a pure function !
Join us to design the following discriminant class, the problem occurs when calling remove_if.
publicbool> {public: BadPredicatetimes(0) {} booloperator()(const Widget&) { return3; }private: int times;};
Let's take a look at the function implementation of REMOVE_IF. It's possible to achieve this.
template<class ForwardIt, class unarypredicate>forwardit remove_if (forwardit first , ForwardIt last , Unarypredicate p) {first = std::find_if (first , Last , p); if (first ! = last ) { return first ; } else {forwardit next = Begin; return remove_copy_if (++next, last , first , p); }}
Originally we wanted to remove the 3rd element, but through the implementation of the remove_if we actually deleted the 6th element as well. Because predicate is passed by value, the function subclass is reconstructed.
In fact, the simplest solution is to always declare operator () as const. This is not enough, because even the const member function can access mutable, a non-const local static object, a non-const class static object, a non-const global object, and so on.
In the example above, even trying to use a static variable is not possible. Because discriminant formula must be pure function!
So, the most important point is! Make sure the discriminant is a pure function!
[C + +] Some suggestions for efficiently defining STL comparison functions