C++11提供了std::function和std::bind两个工具,用于引用可调用对象。这些可调用对象包括 普通函数,Lambda表达式,类的静态成员函数,非静态成员函数以及仿函数等。引用可调用对象,可以用于回调,抽象,以及延迟调用等多种场景。
std::function
1:介绍
类模版std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。
通常std::function是一个函数对象类,它包装其它任意的函数对象,被包装的函数对象具有类型为T1, …,TN的N个参数,并且返回一个可转换到R类型的值。std::function使用 模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为std::function。std::function统一和简化了相同类型可调用实体的使用方式,使得编码变得更简单。
最简单的理解就是:
通过std::function对C++中各种可调用实体(普通函数、Lambda表达式、函数指针、以及其它函数对象等)的封装,形成一个新的可调用的std::function对象;让我们不再纠结那么多的可调用实体。一切变的简单粗暴。
2:原型
template< class R, class... Args >
class function<R(Args...)>
R是返回值类型,Args是函数的参数类型,实例一个std::function对象很简单,就是将可调用对象的返回值类型和参数类型作为模板参数传递给std::function模板类。实例化比如:
std::function<void()> f1;
std::function<int (int , int)> f2;
3:用法
std::function包含于头文件 #include中,可将各种可调用实体进行封装统一,包括
- 普通函数
- lambda表达式
- 函数指针
- 仿函数(functor 重载括号运算符实现)
- 类成员函数
- 静态成员函数
见实例
#include <iostream>
#include <functional>
using namespace std;
std::function<bool(int, int)> fun;
//普通函数
bool compare_com(int a, int b)
{
return a > b;
}
//lambda表达式
auto compare_lambda = [](int a, int b){ return a > b;};
//函数对象类
class compare_class
{
public:
bool operator()(int a, int b)
{
return a > b;
}
};
//类成员函数
class compare
{
public:
bool compare_member(int a, int b)
{
return a > b;
}
static bool compare_static_member(int a, int b)
{
return a > b;
}
};
int main()
{
bool result;
fun = compare_com;
result = fun(10, 1);
cout << "普通函数输出, result is " << result << endl;
fun = compare_lambda;
result = fun(10, 1);
cout << "lambda表达式输出, result is " << result << endl;
fun = compare_class();
result = fun(10, 1);
cout << "函数对象类输出, result is " << result << endl;
fun = compare::compare_static_member;
result = fun(10, 1);
cout << "类静态成员函数输出, result is " << result << endl;
类普通成员函数比较特殊,需要使用bind函数,并且需要实例化对象,成员函数要加取地址符
compare temp;
fun = std::bind(&compare::compare_member, temp, std::placeholders::_1, std::placeholders::_2);
result = fun(10, 1);
cout << "类普通成员函数输出, result is " << result << endl;
}
4:注意事项
可见std::function的使用其实是很简单的,只要创建一个模板类对象,并传入相应的模板参数就可以存储任何具有相同返回值和参数的可调用对象,在调用的时候直接将std::function对象加上()或加如参数就可以调用存储在其中的可调用实体。
-
关于可调用实体转换为std::function对象需要遵守以下两条原则:
转换后的std::function对象的参数能转换为可调用实体的参数;
可调用实体的返回值能转换为std::function对象的返回值。
-
std::function对象最大的用处就是在实现函数回调,使用者需要注意,它不能被用来检查相等或者不相等,但是可以与NULL或者nullptr进行比较。
-
需要注意的是创建的std::function对象中存储的可调用实体不能为空,若对空的std::function进行调用将抛出 std::bad_function_异常。
std::bind
1:介绍
std::bind函数将可调用对象(用法中所述6类)和可调用对象的参数进行绑定,返回新的可调用对象(std::function类型,参数列表可能改变),返回的新的std::function可调用对象的参数列表根据bind函数实参中std::placeholders::_x从小到大对应的参数确定。
实例如下:
#include <iostream>
using namespace std;
class A
{
public:
void fun_3(int k,int m)
{
cout<<k<<" "<<m<<endl;
}
};
void fun(int x,int y,int z)
{
cout<<x<<" "<<y<<" "<<z<<endl;
}
void fun_2(int &a,int &b)
{
a++;
b++;
cout<<a<<" "<<b<<endl;
}
int main(int argc, const char * argv[])
{
auto f1 = std::bind(fun,1,2,3); //表示绑定函数 fun 的第一,二,三个参数值为: 1 2 3
f1(); //print:1 2 3
auto f2 = std::bind(fun, placeholders::_1,placeholders::_2,3);
//表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别有调用 f2 的第一,二个参数指定
f2(1,2);//print:1 2 3
auto f3 = std::bind(fun,placeholders::_2,placeholders::_1,3);
//表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别有调用 f3 的第二,一个参数指定
//注意: f2 和 f3 的区别。
f3(1,2);//print:2 1 3
int n = 2;
int m = 3;
auto f4 = std::bind(fun_2, n,placeholders::_1);
f4(m); //print:3 4
cout<<m<<endl;//print:4 说明:bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的
cout<<n<<endl;//print:2 说明:bind对于预先绑定的函数参数是通过值传递的
A a;
auto f5 = std::bind(&A::fun_3, a,placeholders::_1,placeholders::_2);
f5(10,20);//print:10 20
std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);
fc(10,20);//print:10 20
return 0;
}