1. std::function
类模板 std::function 是通用多态函数封装器。 std::function 的实例能存储、复制及调用任何可调用 (Callable) 目标——函数、 lambda 表达式、 bind 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。存储的可调用对象被称为 std::function 的目标。若 std::function 不含目标,则称它为空。调用空 std::function 的目标导致抛出 std::bad_function_call 异常。它满足可复制构造 (CopyConstructible) 和可复制赋值 (CopyAssignable) 。若需要与类非静态成员绑定需要与std::bind结合使用。
2. std::bind
C++11起使用std::bind进行函数绑定,它可以绑定普通函数、全局函数、静态函数、类静态函数甚至是类成员函数,std::bind在绑定时可以绑定一部分参数,未绑定的参数使用占位符,在运行时再传入参数,它的返回值是一个函数对象,可以赋值给std::function对象。
std::bind函数定义如下:
template< class F, class… Args >
/unspecified/ bind( F&& f, Args&&… args );
参数:
F: 可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针)
args: 绑定的参数列表,未绑定参数为命名空间 std::placeholders 的占位符 _1, _2, _3… 所替换
注意:
如可调用 (Callable) 中描述,调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是引用或指针(可以包含智能指针,如 std::shared_ptr 与 std::unique_ptr),指向将访问其成员的对象。
到 bind 的参数被复制或移动,而且决不按引用传递,除非包装于 std::ref 或 std::cref 。
允许同一 bind 表达式中的多重占位符(例如多个 _1 ).
3. 使用实例
#include<iostream>
#include<functional>
#include<random>
int Sumfun(int a, int b){
return a+b;
}
template <typename T>
T Sumfun2(T a, T b){
return a+b;
}
struct PrintNum{
void operator()(int num)const{
std::cout<<"PrintNum: "<<num<<std::endl;
}
};
class Foo{
public:
Foo(int num):num_(num){}
void Print_add(int value)const{
std::cout<<"Foo Print_add: "<<num_ + value<<std::endl;
}
int num_;
};
int main(int argc, char**argv)
{
///存储自由函数
std::function<int(int,int)> fun_sum = Sumfun;
std::cout<<"sum: "<<fun_sum(1,2)<<std::endl; ///sum: 3
///存储模板函数
std::function<double(double, double)> fun_sum2 = Sumfun2<double>;
std::cout<<"sum2: "<<fun_sum2(1.1,2.9)<<std::endl; ///sum2: 4
///存储函数对象
std::function<void(int)> fun_printnum = PrintNum();
fun_printnum(50); ///PrintNum: 50
///存储lambda
std::function<void(double)> fun_lambda = [](double num)->void{std::cout<<"fun_lambda: "<<num<<std::endl;};
fun_lambda(60.7); ///fun_lambda: 60.7
////存储到数据成员访问器的调用
std::function<int(Foo const&)> fun_foo_num = &Foo::num_;
std::cout<<"Foo num: "<<fun_foo_num(10)<<std::endl; ///Foo num: 10
///存储到成员函数的调用
std::function<void(const Foo&, int)> fun_foo = &Foo::Print_add;
Foo foo(7);
fun_foo(foo, 4); ///Foo Print_add: 11
fun_foo(8,7); ///Foo Print_add: 15
////// std::function与std::bind结合使用
/// std::bind绑定普通函数
std::function<int()> fun_bind = std::bind(Sumfun, 3, 6); ///绑定输入第一个参数为3,第二个参数为6
std::cout<<"sum3: "<<fun_bind()<<std::endl;
///存储到类成员函数及对象的调用
///std::placeholders::_1为占位符,表示将来传递给fun_bind_foo1的参数
std::function<void(int)> fun_bind_foo1 = std::bind(&Foo::Print_add, foo,std::placeholders::_1); ///foo 按值传递
std::function<void(int)> fun_bind_foo2 = std::bind(&Foo::Print_add, &foo,std::placeholders::_1); ///foo 按指针传递
foo.num_ = 6;
fun_bind_foo1(5); ///Foo Print_add: 12 (5 + 7)
fun_bind_foo2(5); ///Foo Print_add: 11 (5 +6)
//// 函数参数的重新排序
int n = 7;
auto fun2 = std::bind(fun1, std::placeholders::_2, std::placeholders::_1,
std::placeholders::_1, std::cref(n), n);
n = 20;
fun2(1, 2,33333); ///2 1 1 20 7
/// std::placeholders::_2 表示传入参数中的第二个参数, std::placeholders::_1表示传入参数中的第一个参数
/// 传入的第三个参数 33333未被使用
///第4个参数按引用传递,第5个参数按值传递
/// 嵌套 bind 子表达式共享占位符
auto f2 = std::bind(fun1, std::placeholders::_3, std::bind(g, std::placeholders::_2),
std::placeholders::_1, 4, 5);
f2(10, 11, 12); /// 12 11 10 4 5
/// 进行到 fun1(12, g(11), 10, 4, 5); 的调用
printf("fun1 add: %p\n",fun1); ///fun1 add: 0x402ee0
printf("f2 add: %p\n",f2); ///f2 add: 0x7ffcc5b9e890 这两个函数具有不同的内存地址
///常见使用情况:以分布绑定 RNG
std::default_random_engine e;
std::uniform_int_distribution<> d(0, 10);
auto rnd = std::bind(d, e); // a copy of e is stored in rnd
for(int n = 0; n < 10; ++n)
std::cout << rnd() << ' ';
std::cout << '\n'; ///0 1 8 5 5 2 0 7 7 10 生成[0~10]的随机序列
return 0;
}
参考
https://zh.cppreference.com/w/cpp/utility/functional/function
https://zh.cppreference.com/w/cpp/utility/functional/bind