目录
1、为什么需要包装器?
2、包装器的声明和使用
(1) 声明
(2) 实际应用
(3) 包装器接收类成员函数
3、包装器的绑定:bind函数
(1) 调整参数顺序
(2) 调整参数个数
1、为什么需要包装器?
函数模板可以接收各种不同类型的参数,即便是普通函数、仿函数、lamda表达式也可以接收,但是在实例化的时候,即便形参和返回值完全一样,因为是不同类型,所以需要实例化多份代码。
所以包装器的作用是统一传入的函数对象(前提是不同函数的形参和返回值都要一样),最终只需实例化出一份代码,从而达到提高效率的目的。
// 要传入的函数 / 表达式
int func_add(int x, int y)
{
return x + y;
}
struct Add {
int operator()(int x, int y)
{
return x + y;
}
};
auto lamda_add = [](int x, int y) -> int {
return x + y;
};
// 函数模板
template<class F>
void test_add(F f, int x, int y)
{
cout << typeid(F).name() << endl;
f(x, y);
}
2、包装器的声明和使用
(1) 声明
包装器的作用是为了统一传递给模板的函数对象,不同函数的形参和返回值必须一致。
头文件:functional
语法格式:std::function<返回值(形参类型1,形参类型2,...)> = 函数名 / 函数对象
// 函数func_add,对应的包装器f1
function<int(int, int)> f1 = func_add;
test_add(f1, 10, 20);
// 仿函数Add(),对应的包装器f2
function<int(int, int)> f2 = Add();
test_add(f2, 10, 20);
// lamda表达式lamda_add,对应的包装器f3
function<int(int, int)> f3 = lamda_add;
test_add(f3, 10, 20);
我们会发现不同函数经过包装以后,函数对象的类型都是 function<int(int, int)>,我们再看一下实际调用的结果。
(2) 实际应用
有了包装器,我们可以使用不同的指令来调用不同的函数,以最简答的四则运算为例3
(3) 包装器接收类成员函数
包装器其实也可以接收类的成员函数,包装器接收的是类成员对象的函数地址,使用包装器时需要多传递一个匿名对象,因为实际上是使用匿名对象去调用类成员函数。
class Adds {
public:
int add(int x, int y) {
return x + y;
}
};
int main()
{
function<int(Adds, int, int)> f4 = &Adds::add; // 注意这里必须要取地址
f4(Adds(), 10, 20);
}
结合下面介绍的绑定bind,我们可以在声明的时候就让包装器绑定匿名对象,在实际调用的时候,我们就无需传递匿名对象了。
function<int(int, int)> f4 = bind(&Adds::add, Adds(), placeholders::_1, placeholders::_2);
// f4(10, 20);
test_add(f4, 10, 20);
3、包装器的绑定:bind函数
包装器的绑定主要用于调整参数,比如调整顺序、调整参数个数。上面使用包装器接收类的成员函数便是bind的一种应用场景。
既然是调整参数,bind函数是如何获取到包装器对应的函数参数呢?
- placeholders::_1 表示函数的第一个参数
- placeholders::_2 表示函数的第二个参数
- ...
- 以此类推,bind函数最多可以调整20个参数
(1) 调整参数顺序
首先,我们定义一个简单的lamda表达式,打印传入的内容;
其次,我们使用bind函数调整第一个参数和第二个参数的顺序,再次打印传入的内容。
auto print = [](int x, int y) -> int{
cout << x << " " << y << endl;
return 0;
};
cout << "调整参数之前:";
print(10, 20); // 调整参数之前
function<int(int, int)> func = bind(print, placeholders::_2, placeholders::_1);
cout << "调整参数之后:";
func(10, 20); // 调整参数之后
(2) 调整参数个数
调整参数个数,其实也可以理解为设置缺省值,设置缺省值以后,有些参数就无需重复传递参数
class Adds {
public:
int add(int x, int y) {
return x + y;
}
};
// 正常包装
function<int(Adds, int, int)> f4_1 = &Adds::add; // 注意这里必须要取地址
f4_1(Adds(), 10, 20);
// 修改参数个数后包装 (将对象设为缺省)
function<int(int, int)> f4_2 = bind(&Adds::add, Adds(), placeholders::_1, placeholders::_2);
f4_2(10, 20);