C++11 -- Wrappers

function wrapper

The concept of function wrappers

A function wrapper, also called an adapter, is essentially a class template.
For example:

1 template< class R, class... Args >
2 class function< R (Args...)>

Explanation :
(1): R is the return type of the called function Args...is the formal parameter of the called function, essentially a parameter pack.

(2): function is a class template with only member functions and no data members.

The use of functions

The function wrapper can wrap ordinary functions, function objects, lambda expressions, and member functions in classes. The following wrapping calls for various functions and related precautions are as follows:

double f(double a, double b)
{
    
    
	return a + b;
}
struct Functor
{
    
    
public:
	int operator()(int a, int b)
	{
    
    
		return a + b;
	}
};
class Plus
{
    
    
public:
	static int Plusi(int a, int b)         //静态成员函数
	{
    
    
		return a + b;
	}
	double Plusd(double a, double b)      //成员函数
	{
    
    
		return a + b;
	}
};
int main()
{
    
    
	//包装函数指针
	function<double(double, double)> funcf = f;  //传递函数名
	cout << funcf(1.1, 2.2) << endl;

	//包装仿函数
	function<int(int, int)> funcFunctor = Functor();//传递仿函数对象
	cout << funcFunctor(11, 22) << endl;

	//包装lambda表达式
	function<int(int, int)> funcLmd = [](int a, int b) {
    
    return a + b;}; //传递匿名对象
	cout << funcLmd(11, 22) << endl;

	//静态成员函数
	function<int(int, int)> funcPlusi = Plus::Plusi;  //标明类域,传递函数名.
	cout << funcPlusi(11, 22) << endl;

	//非静态成员函数
	function<double(Plus, double, double)> funcPlusd = &Plus::Plusd; //增加&,标明类域,传递函数名.

	cout << funcPlusd(Plus(),3.3,4.4)<< endl;        //因为类的成员函数需要对象的调用,所以必须传递额外传递一个对象.                        
	return 0;
}

function instantiation

Wrappers can solve the inefficiency caused by instantiating multiple copies of templates.
For example:

  • In the useF template, there are two parameters, the first parameter can receive various function types, and the second parameter can receive various information.
  • A static variable is defined in the useF function template. If multiple useF function templates are instantiated, the static variable count will not be the same variable.

The code is as follows :

template<class F, class T>
T useF(F f, T x)
{
    
    
	static int count = 0;
	cout << "count: " << ++count << endl;

	cout << "count: " << &count << endl;
	return f(x);
}
double f(double i)
{
    
    
	return i / 2;
}
struct Functor
{
    
    
	double operator()(double d)
	{
    
    
		return d / 3;
	}
};
int main()
{
    
    
	//函数指针
	cout << useF(f, 11.11) << endl;

	//仿函数
	cout << useF(Functor(), 11.11) << endl;

	//lambda表达式
	cout << useF([](double d)->double {
    
    return d / 4; }, 11.11) << endl;
	return 0;
}

The result of the operation is as follows :

  • It can be seen that here, because we pass three different types of functions to the first parameter T of the useF function template, the useF function template is instantiated three times during the compilation phase, because the static variables are three different variable.
  • When using three different functions as actual parameters to pass and call the useF function template, in fact, different useF functions are called once, which leads to different static variables in the useF function, so the count is only increased once.
  • However, since the return value data types and formal parameter data types of the three function types we pass are the same, other data types are also the same in the execution of the useF function, so there is no need to instantiate three useF functions at all. Therefore
    insert image description here
    , The wrapper can solve the problem of inefficiency caused by multiple template instantiations due to this situation.
int main()
{
    
    
	//函数名
	function<double(double)> func1 = func;
	cout << useF(func1, 11.11) << endl;

	//函数对象
	function<double(double)> func2 = Functor();
	cout << useF(func2, 11.11) << endl;

	//lambda表达式
	function<double(double)> func3 = [](double d)->double {
    
    return d / 4; };
	cout << useF(func3, 11.11) << endl;
	return 0;
}


The running effect is as follows :

  • Since the static variables in the useF class template are the same, it can be judged that after function packaging, only one copy of the useF function template is instantiated, which greatly improves the efficiency of the function template.
  • When we pass three different function types and call them, we actually call the same useF function three times and add up the static variable count three times.
    insert image description here

Use function to solve reverse Polish expressions

bind wrapper

The essence of bind is a function template, which is like a function wrapper (adapter), which can receive a callable object and generate a new callable object to "adapt" to the parameter list of the original object.

Introduction to bind wrappers

The bind prototype is as follows:

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

Note :
( 1 ): fn refers to the object that needs to be wrapped.

( 2 ): args...corresponds to the parameters in the given fn function.

The general form of calling bind is as follows:

auto newCallable = bind(callable,arg_list);

( 1 ) callable: the object that needs to be wrapped...

( 2 ) newCallable: A new callable object generated.

( 3 ) arg_list: arg_list is a comma-separated parameter list corresponding to the parameters of the given callable. When we call newCallable, newCallable will call callable and pass the arg_list parameter list to callable.

Note :
The parameters in arg_list may contain names of the form _n, where n is an integer, these parameters are "placeholders", representing the parameters of newCallable, and they occupy the "position" of the parameters passed to newCallable. For example: _1 is the first parameter of newCallable, _2 is the second parameter, and so on.

bind adjusts the order of function parameter passing

We use bind to bind the Mul function to adjust the order of parameter passing.

int Mul(int a, int b, int rate)
{
    
    
	return (a - b) * rate;
}
class Sub
{
    
    
public:
	int sub(int a, int b)
	{
    
    
		return a - b;
	}
};
int main()
{
    
    
	int x = 3, y = 2, z = 1;
	
	cout << Mul(x, y, z) << endl;

	cout << "-----------------------------------------------\n" << endl;

	auto funcMul = bind(Mul, _3, _2, _1);

	cout << funcMul(3, 2, 1) << endl;
}
  • It can be seen that when we use bind to reverse the order of the placeholders _1, _2, and _3, we pass the actual parameters x, y, and z to the Mul function again. At this time, x is passed to _1 and y is passed to _2 , z is passed to _3.
  • However, at this time, the Mul parameters referred to by the placeholders _1, _2, and _3 have changed. _1 points to rate instead of a, _2 still points to b, and _3 points to a instead of rate.
  • That is: when we call the funcMul object to pass the actual parameters x, y, and z, at this time x is passed to the formal parameter rate, y is passed to b, and z is passed to a.
    insert image description here

bind binding function fixed parameters

Why use bind to bind fixed parameters?

Because after using bind to bind fixed parameters, you can call the original object by generating a new object with fewer formal parameters after wrapping with bind.

For example :
In the following example, for the member function, the newly generated object call needs to be wrapped by bind, because the member function needs an object to be called, so we need to pass one more parameter than the global member function, but when we use wrapping When the wrapper bind wraps the global member function and the ordinary member function at the same time, then the wrapper cannot judge the specific number of received parameters at all.
insert image description here
The code is as follows:

int Plus(int a, int b)
{
    
    
	return a / b;
}

}
class Sub
{
    
    
public:
	int sub(int a, int b)
	{
    
    
		return a - b;
	}
};
int main()
{
    
    
	function< int(int, int)> funcPlus = Plus;

	function< int(int, int)>funcSub = bind(&Sub::sub, Sub(), _1, _2);

	map<string, function<int(int, int)>> funcMap =
	{
    
    

		{
    
    "+",Plus},
		{
    
    "-",bind(&Sub::sub,Sub(),_1,_2) }
	};

	cout << funcSub(2, 1) << endl;
	
	cout << funcMap["-"](2, 1) << endl;
}
  • After the three parameters to be received in the member function are bound by the bind adapter, only a sub() anonymous object directly passed after &Sub::sub is required to bind the parameters during definition, and then in subsequent calls, only You need to pass the formal parameters required by the corresponding function.
  • This also perfectly solves the problem that bind wraps the member function of the class at the same time and the global function receives parameters that do not match. Another
    insert image description here
    example :
    when we use the bind wrapper to bind the first parameter of the Mul function again, the default a is already After passing the actual parameters, you only need to pass two actual parameters to b and rate in the formal parameters of the Mul function when calling.
    insert image description here

Guess you like

Origin blog.csdn.net/m0_63300413/article/details/130944702