C++11的bind函数
参数绑定
如果一个函数只在一两个地方操作,使用Lambad很方便,如果在很多地方操作,编写多次相同的Lambda函数就很不方便,需要定义函数。但是,定义的函数又不能完全代替能捕捉局部变量的Lambda函数,所以就出现了bind函数。
bind函数是C++11标准库的函数,需要添加头文件(g++下加-std=c++11)
#include <functional>
bind就是一个通用的函数适配器,它可以接受一个可调用函数,生成一个新的可调用对象来“适应”原函数的参数列表。
std::bind函数
调用bind函数的方式为:
auto newCallable = bind(callable, arg_list);
说明:
callable
: 本身就是一个可调用的函数
arg_list
: 用逗号分割的参数列表,对应给定的callable参数
当我们调用newCallable时,会调用callable函数,并且将arg_list的参数传递给callable函数。
参数占位符
arg_list中有许多占位符,形如_n的名字,其中n是一个整数,表示newCallable的参数的位置比如:_1为newCallable的第一个参数,_2为第二个参数...。
举例说明:
现在有一个函数:
bool checkSize(const string &s, string::size_type sz)
{
return s.size() >= sz;
}
使用bind生成一个调用checkSize的对象:
auto check = bind(checkSize, _1, 6);
其中的_1表示需要给第一个参数传递一个const string&。
string s = "hello";
bool b1 = check(s); //check(s) 会调用checkSize(s, 6)
使用bind,可以将原来的基于Lambda的find_if调用:
auto wc = find_if(words.begin(), words.end(), [sz](const string &a));
改变为使用checkSize版本:
auto wc = find_if(words.begin(), words.end(), bind(check_size, _1, sz));
_n参数说明
\_n
参数都定义在一个名字为placeholders
命名空间中,这个命名空间又在std命名空间中。那么\_1
就是:
using std::placeholders::_1;
对每一个占位符都要使用using声明,非常麻烦,可以使用:
using namespace std::placeholders;
placeholders的明明空间也定义在functional.h
头文件中
bind的参数顺序
bind绑定的给定函数的参数的顺序。比如func是一个函数,它有5个参数,下面对bind的调用:
auto g = bind(fun, a, b, _2, c, _1);
生成一个新的可调用对象,有两个占位符,传递给g的第一个参数绑定到_1,第二个参数绑定到_2,bind调用会将g(_1, _2)
映射为f(a, b, _2, c, _1)
,如:调用g(X, Y)
就会调用f(a, b, Y, c, X)
;
绑定引用参数
默认情况下,bind的非占位符的参数是拷贝到bind返回的可调用的函数中,与Lambda类似,有时候绑定的参数希望用引用的方式传递,或不让绑定的参数类型拷贝。
例如:
for_each(words.begin(), words.end(), [&os, c](const string &s) {os << s << c;});
可以编写一个函数完成同样的功能:
ostream &print(ostream &os, const strint &s, char c)
{
return os << s << c;
}
但是:不能直接用bind来代替os的捕获:
for_each(words.begin(), words.end(), bind(print, os, _1, ' ');
由于不能拷贝一个ostream。如果我们希望bind一个函数而不希望拷贝参数,就需要用标准库的ref
函数。
for_each(words.begin(), words.end(), bind(printf, ref(os), _1, ' '));
函数ref返回一个对象,包含给定的引用,此对象是可以拷贝的。标准库中还有cref函数,生成一个保存const引用的类。ref和cref都在头文件"functional.h"
中
关于bind1st和bind2nd函数
标准库中定义了这两个函数,类似于bind,但是这两个函数只能分别绑定第一个或第二个参数。由于局限性太强,在C++11中已经被弃用,新的C++应该使用bind。