文章目录
- function包装器
- function包装器的概念
- function的运用
- function实例化
- 使用function解决逆波兰表达式
- bind包装器
- bind包装器相关介绍
- bind绑定函数固定参数
function包装器
function包装器的概念
function包装器,也叫做适配器,它的本质是一个类模板.
例如:
1 template< class R, class... Args >
2 class function< R (Args...)>
说明:
( 1 ) :R是被调用函数的返回类型 Args…是被调用的函数的形参,本质上是一个参数包.
( 2 ): function是类模板,只有成员函数,没有数据成员。
function的运用
function包装器可以对普通函数,函数对象,lambda表达式,类中的成员函数进行包装,以下针对各类函数进行包装调用以及相关注意事项如下:
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实例化
包装器可以解决模板实例化多份而造成的效率低下的问题.
例如:
- 在useF模板中,有两个参数,第一个参数可以接收各种函数类型,第二个参数可以接收各种信息.
- useF函数模板中的定义了一个静态变量,如果实例化出多分useF函数模板,静态变量count也将不是同一个变量.
代码如下:
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;
}
运行结果如下:
- 由此可见,在这里由于我们对useF函数模板的第一个参数T传递了三种不同类型的函数,进而导致useF函数模板在编译阶段实例化出了三份,因为静态变量为三个不同的变量.
- 在使用三种不同函数作为实参传递并调用useF函数模板,实际上是分别调用了一次不同的useF函数,进而导致useF函数中的静态变量不是同一个,所以count只增加了一次.
- 但是由于我们所传的三种函数类型的返回值数据类型和形参数据类型是一样的,在执行useF函数中,其他数据类型也相同,所以根本就没必要实例化三份useF函数.
所以,包装器可以解决因为该情况而导致模板实例化多份造成效率低下的问题.
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;
}
运行效果如下:
- 由于useF类模板中的静态变量相同,由此可以判断经过function包装过后,useF函数模板只实例化出了一份,这极大地提高了函数模板的效率.
- 当我们传递三种不同的函数类型并进行调用时,实际上就是调用了三次同一个useF函数,对静态变量count累加了三次.
使用function解决逆波兰表达式
bind包装器
bind的本质是一个函数模板,它就像一个函数包装器(适配器),可以接收一个可调用对象的,从而生成一个新的可调用对象来"适应"原对象的参数列表.
bind包装器相关介绍
bind原型如下:
// 原型如下:
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);
注意:
( 1 ): fn 指的是需要包装的对象.
( 2 ): args…对应的是给定fn函数中的参数.
调用bind的一般形式如下:
auto newCallable = bind(callable,arg_list);
( 1 ) callable: 需要包装的对象…
( 2 ) newCallable: 生成的一个新的可调用对象.
( 3 ) arg_list: arg_list是一个逗号分隔的参数列表,对应给定的callable的参数.当我们调用newCallable时,newCallable会调用callable,并将arg_list参数列表传给callable中.
注意:
arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。例如: _1为newCallable的第一个参数,_2为第二个参数,以此类推.
bind绑定函数固定参数
为什么要使用bind绑定固定参数呢?
因为使用bind绑定固定参数后,可以通过bind包装后生成新的形参较少的对象来调用原来的对象.
例如:
在以下例子中,对于成员函数来说,要通过bind包装新生成的对象调用,因为成员函数需要对象才能调用,所以我们要比全局成员函数要额外多传递一个参数,但是当我们使用包装器bind同时包装全局成员函数和普通成员函数时,那么包装器此时根本无法判断接收参数的具体个数.
代码如下:
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;
}
- 将成员函中要接收三个参数经过bind适配器绑定后,在定义时只需要在&Sub::sub后面直接传递的一个sub()匿名对象进行该参数绑定,然后在以后的调用时,只需要传递对应的函数所需要的形参就可以了.
- 这样也完美的解决了bind同时包装类的成员函数和全局函数接收参数不匹配的问题.