std::bind函数定义在头文件functional中,是一个函数模板,它就像一个函数适配器,接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收n个参数的函数fn,通过绑定一些参数,返回一个接收m个(m可以大于n,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。
std::bind函数有两种函数原型,定义如下:
template< class f, class... args >
/*unspecified*/ bind( f&& f, args&&... args );
template< class r, class f, class... args >
/*unspecified*/ bind( f&& f, args&&... args );
std::bind返回一个基于f的函数对象,其参数被绑定到args上。
f的参数要么被绑定到值,要么被绑定到placeholders(占位符,如_1, _2, ..., _n)。
f:一个可调用对象(可以是函数对象、函数指针、函数引用、成员函数指针、数据成员指针),它的参数将被绑定到args上。
args:绑定参数列表,参数会被值或占位符替换,其长度必须与f接收的参数个数一致。
调用std::bind的一般形式为:
auto newcallable = std::bind(callable, arg_list);
其中,newcallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。即,当我们调用newcallable时,newcallable会调用callable,并传递给它arg_list中的参数。
std::bind的返回类型是一个未指定类型t的函数对象,这个类型t满足以下条件: std::is_bind_expression
t包含成员:
1.对象成员
一个由std::forward
2.构造函数
如果t的所有对象成员都是可拷贝的,则它自身也是可拷贝的;如果它的所有对象成员都是可移动构造的,则它自身也是可移动构造的。
3.成员类型result_type(从c 17开始result_type已经被弃用)
·如果f是函数指针或者成员函数指针,result_type就是f的返回值类型
·如果f是一个拥有(或者说定义了)result_type的类类型,那么t的result_type就是f::result_type,即使result_type已经在t中被定义过
4.成员函数operator()
这是最应该了解的,因为在实际使用过程中,我们调用std::bind得到的返回值就是用来作为函数调用的。
bind的返回值t,假设我们这样调用:g(a1, a2, a3, … ai); 此时g内部保存的std::decay
·如果调用bind时指定的是reference_wrapper
·如果在创建g时,使用了嵌套的bind,即g = bind(fn, args…)的参数列表args中,存在某个arg:使得std::is_bind_expression
·如果在创建g时,使用了占位符placeholders, 即 g = bind(fn, arg1, arg2, …, _1, _2, …), (对于_1, _2…, 有std::is_placeholder
否则,ret内部保存的args,即上文提到的_mybargs(bind调用时绑定的参数们)将被以左值的形式传给_myfun以完成调用,这些参数和g有相同cv限定属性.
如果g(a1, a2, …, ai)中,有哪些ai没有匹配任何的placeholders,比如在调用bind时,placeholder只有_1, 而g(a1, a2, a3), 那么a2, a3就是没有匹配的,没有被匹配的参数将被求值,但是会被丢弃。
如果g被指定为volatile(volatile or const volatile),结果是未定义的。
上述内容都可以在c 文档中找到。
从实践出发,看下面一段程序来理解std::bind如何使用:
#include
#include
void fn(int n1, int n2, int n3) {
std::cout << n1 << " " << n2 << " " << n3 << std::endl;
}
int fn2() {
std::cout << "fn2 has called.\n";
return -1;
}
int main()
{
using namespace std::placeholders;
auto bind_test1 = std::bind(fn, 1, 2, 3);
auto bind_test2 = std::bind(fn, _1, _2, _3);
auto bind_test3 = std::bind(fn, 0, _1, _2);
auto bind_test4 = std::bind(fn, _2, 0, _1);
bind_test1();//输出1 2 3
bind_test2(3, 8, 24);//输出3 8 24
bind_test2(1, 2, 3, 4, 5);//输出1 2 3,4和5会被丢弃
bind_test3(10, 24);//输出0 10 24
bind_test3(10, fn2());//输出0 10 -1
bind_test3(10, 24, fn2());//输出0 10 24,fn2会被调用,但其返回值会被丢弃
bind_test4(10, 24);//输出24 0 10
return 0;
}
过程合法性分析
设f需要的参数个数为n, bind(f…)中,提供的值的个数为v, 提供的占位符个数为s。对于合法的bind调用,必有 n == v s. 如果v s 超出n或者小于n, 编译都会报错。
bind返回值的传参调用
·参数个数
f的调用中提供的参数与占位符数量有关,从程序中可以看出。
·参数顺序
参见程序运行结果,参数顺序与std::placeholders中的顺序一致,因此我们可以用bind来重排参数顺序。
这些只是std::bind的基本用法,对std::bind的引入是c 11的一大亮点,将其与lambda表达式、智能指针、绑定引用参数等知识相结合会明显改变原有的代码编写。std::bind的高级用法还需要更深入学习。