一、为什么要用std::bind?
- 满足算法要求
- 重排参数顺序
二、如何使用std::bind?
bind
函数头文件为functional
,bind
函数的定义如下:
auto newCallable=bind(callable,arg_list);
- callable原函数,也就是待绑定的函数;
- arg_list参数列表。它可以是占位符,也可以是具体的参数(比如1)。无论如何从个数上来说arg_list的个数应该等于原函数个数,占位指的是我现在无法知道你切确的数值,但是我为接下来调用占住了位置,这个参数实际的值由序号在调用函数中找到并传入对应的位置。
arg_list
的参数个数必须和原映射参数一样多- _n 最大支持个数是
20
- 假如
arg_list
最大序号是_x,那么调用新映射至少应该和x一样多 - 头文件在
functional
- _1,_2,…_n需要调用
using namespace std::placeholders;
举个简单的例子:
void fun(int a, int b, int c)
{
cout << "a = " <<a<< endl;
cout << "b = " <<b<< endl;
cout << "c = " <<c<< endl;
}
int main()
{
auto newfun = bind(fun,1, std::placeholders::_5, 3);
newfun(2, 3, 4, 3, 4396);
}
输出的结果为:
bind函数的第一个参数是目标函数,接着就是实参或者placeholder参数。newfun在绑定阶段给与oldfun默认实参,在调用阶段将指定的holder编号对应的参数传递给oldfun完成调用。
bind这种机制有以下用途:
- 用当前代码可见变量具体化某个函数,实现类似函数重载的功能
- placeholder的编号重排了参数位置
对于第一个用途,有一个与之相似的语法:lambda表达式,它也可以通过捕获操作得到环境变量。
三、实例分析
例子一:增加算法灵活性
3.1 find_if
对参数要求严格,灵活性差
下面这个函数目的是找出容器中值大于300对应的迭代器位置。
int main()
{
vector<int> ivec{ 3,4,5,61,312,5,432,1320,1,1 };
int sz = 400;
auto i = find_if(ivec.begin(), ivec.end(), fff);
cout<<distance(ivec.begin(), i);
}
bool fff(int a)
{
return a>300;//当ivec中元素满足这一条件时,算法返回迭代器
}
运行结果为:4
,符合预期。find_if
完成了指定的任务(找出大于300的迭代器位置),新任务来了,查找大于432
的下标时,于是我们重新写了一个谓词:
bool fff(int a)
{
return a>432;//当ivec中元素满足这一条件时,算法返回迭代器
}
结果为:7
,由于种种原因,我们可能既需要大于300
的,又需要大于432
,甚至需要大于1000
。那还不简单!增加一个阈值参数不就行了!
bool fff(int a,int sz)
{
return a>sz;
}
可惜了!find_if
算法只接受一元谓词,那有没有什么办法可以解决?有!其实还不少:
- 阈值设置为全局变量
lambda
表达式
auto i=find_if(ivec.cbegin(),ivec.cend(),[sz](int a){return a>sz;});
std::bind
半实例化函数
使用std::bind
捕获阈值,实现方法如下:
auto i=find_if(ivec.cbegin(),ivec.cend(),std::bind(fff,_1,sz));
bool fff(int a,int sz)
{
return a>sz;
}
小结:函数定义完成后,传入参数已经确定,就函数内变量而言,函数是“封闭”、“静态”的,封闭在函数作用域中。有几种方法让函数具有“动态性”,分别是全局变量、lambda表达式和bind方法。动态的函数会随着程序的运行而改变其功能,非常不错的性质。
例子二:重排参数顺序
sort(word.begin(),word.end(),isStorter);//升序
sort(word.begin(),word.end(),bind(isStorter,_2,_1));//降序
例子三:绑定引用参数
默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中。假如我们输出一个vector<string>
字符串容器的所有内容,并要求一个string
后跟一个空格,最简单的方法就是使用lambda表达式,捕获os
和c
char c=' ';
std::cout os;
for_each(word.begin(),word.end(),[&os,c]{os<<s<<c;});
当然它可以用函数代替:
ostream &print(ostream &os,const string &s,char c)
{
return os<<s<<c;
}
下面的做法是错误的:
for_each(word.begin(),word.end(),bind(print,os,_1,' ');
bind函数默认传参形式是拷贝,将非占位参数os和’ '拷贝,显然我们的os是不能拷贝的,故不正确,如果我们想要bind建立的映射不是以拷贝形式传入原函数,那么请使用std::ref!
for_each(word.begin(),word.end(),bind(print,std::ref(os),_1,' ');