一、可调用对象
可调用对象有如下几种定义:
- 是一个函数指针,参考 C++ 函数指针和函数类型;
- 是一个具有operator()成员函数的类的对象;
- 可被转换成函数指针的类对象;
- 一个类成员函数指针;
C++中可调用对象的虽然都有一个比较统一的操作形式,但是定义方法五花八门,这样就导致使用统一的方式保存可调用对象或者传递可调用对象时,会十分繁琐。C++11中提供了std::function和std::bind统一了可调用对象的各种操作。
// 普通函数
int add(int a, int b)
{
return a + b;
}
// lambda表达式
auto mod = [](int a, int b)
{
return a + b;
};
// 函数对象类
struct minus
{
int operator()(int a, int b)
{
return a - b;
}
};
上述三种可调用对象虽然类型不同,但是共享了一种调用形式:
int(int ,int)
通过std::function可以将上述类型保存下来:
std::function<int(int, int)> addFunc = add;
std::function<int(int, int)> modFunc = mod;
std::function<int(int, int)> minusFunc = minus();
二 std::function
- std::function 是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
- 定义格式:std::function<函数类型>。
- std::function可以取代函数指针的作用,因为它可以延迟函数的执行,特别适合作为回调函数使用。它比普通函数指针更加的灵活和便利。
三 std::bind
它的主要功能就是作为函数适配器,它接受一个可调用对象,并生成一个新的可调用对象。同时,std::bind可以绑定参数(类似std::thread),并且可以使用占位符代替,其做法很类似于数据查询时所使用的使用占位符先解析。std::bind主要有以下两个作用:
- 将可调用对象和其参数绑定成一个仿函数;
- 只绑定部分参数,减少可调用对象传入的参数。
3.1 绑定普通函数
#include <iostream>
#include <functional>
int add(int a, int b)
{
return a + b;
}
int main()
{
/* ===== 绑定默认参数 ===== */
auto defaultAdd = std::bind(add, 5, 6);
std::cout << defaultAdd() << std::endl;
/* ===== 使用一个占位符 ===== */
auto placeholderAdd = std::bind(add, std::placeholders::_1, 6);
std::cout << placeholderAdd(5) << std::endl;
return 0;
}
与std::thread相似,第一个参数是函数名,普通函数做实参时会隐式转换为函数指针。因此:
auto defaultAdd = std::bind(add, 5, 6);
// equal
auto defaultAdd = std::bind(&add, 5, 6);
3.2 绑定成员函数
#include <iostream>
#include <functional>
class Cal
{
public:
int add(const int& a, const int& b)
{
return a + b;
}
};
int main()
{
Cal calculator;
auto callBind = std::bind(&Cal::add, &calculator, std::placeholders::_1, std::placeholders::_2);
// auto callBind = std::bind(&Cal::add, calculator, std::placeholders::_1, std::placeholders::_2);
// auto callBind = std::bind(&Cal::add, Cal(), std::placeholders::_1, std::placeholders::_2);
std::cout << callBind(5, 1) << std::endl;
return 0;
}
这里需要注意,注释部分也是可行的,对象以拷贝(Copied)的方式被传入std::bind对象。但是,对于不可以以拷贝方式传入的对象(std::unique_ptr一类)需要额外注意。
下面说明上述代码中的bind参数:
- 第一参数为指向成员函数的指针。其可以使用&Fname,也可以使用std::mem_fn(&Fname)对象。
- 第二参数为成员函数指针所属的具体对象。
3.3 绑定引用参数
#include <iostream>
#include <functional>
int add(int a, int& b)
{
return a + b++;
}
int main()
{
int param{ 5 };
auto addFunc = std::bind(&add, 0, std::ref(param));
addFunc();
std::cout << param << std::endl;
return 0;
}
千言万语汇成一句:使用std::ref,而不是直接传入对象。
3.4 附言
不要尝试用std::bind去绑定std::unique_ptr,其实是我不会。
Comments | NOTHING