简述泛型算法之 三bind_参数绑定

来源:互联网 发布:潭州学院java视频下载 编辑:程序博客网 时间:2024/05/16 14:29

简述泛型算法之 三bind 参数绑定

lambda不足

在上篇博客简述泛型算法之 二lambda表达式中提到lambda有不足之处。何以见得?
1) 对于那种只在一两个地方使用的简单操作,lambda表达式是最有用的。但如果我们要在多处使用相同的算法,通常应该定义一个函数,而不是编写相同的lambda表达式。
2)如果一个操作需要很多语句完成,通常使用函数会更好。

如果lambda的捕获列表为空,则用函数代替很容易;
但如果对于捕获函数列表的lambda,用函数代替就不容易了。

我们围绕一个问题详细展开:
ex:比较一个string和一个给定大小

bool check_size(const string& s, string::size_type sz) {    return s.size() >= sz;}

用lambda表达式很容易实现:

find_if(v.begin(), v.end(),        [sz](const string& s) {            return s.size() >= sz;        });

但用函数却不行,因为无法向算法传递一个参数。为了用check_size代替lambda,则必须解决如何向sz形参传递一个参数的问题。

标准库函数bind

bind是一个通用的函数适配器(function adaptor),接受一个可调用对象,生成一个新的可调用对象来适应元对象的参数列表。
---进入关键地带了--(摘自候捷STL源码剖析)

bind一般形式

auto new_callable = bind(callable, arg_list);
1) new_callable本身是一个可调用对象,arg_list时参数列表,对应callable中的参数。即,当我们调用new_callable时,new_callable会调用callable,并传递给它arg_list中的参数。
2) arg_list中参数可能包含形如_n的名字,n是一个整数。这些参数是“占位符”,表示new_callable的参数。数值n表示生成的可调用对象中参数的位置:_1为new_callable的第一个参数,,,

绑定check_size的sz参数

对于该函数:

bool check_size(const string& s, string::size_type sz) {    return s.size() >= sz;}

使用bind来生成一个调用check_size的对象;
auto check_sz = bind(check_size, _1, sz);

此处,我们可以直接调用check_sz

string s = "hello";bool b1 = check_sz(s);

结合find_if使用:
find_if(v.begin(), v.end(), bind(check_size, _1, sz));

使用placeholder名字

名字_n都定义在一个名为placeholder的命名空间中,而这个命名空间又定义在std中;为了使用这些命名空间,两者都要写上;
ex:对于_1的using声明:
using std::placeholders::_1

标准库规定,对于每个占位符,都必须提供一个单独的声明;这样很繁琐,为了简单且不易出错,可以用另一种形式的using语句:
using namespace std::placebolders;

bind的参数

1) 可以用bind绑定给定可调用对象中的参数
ex:假定f可调用,有5个参数
auto g = bind(f, a, b, _2, c, _1);
生成一个可调用对象,它有两个参数分别用占位符_2,_1表示。
g(_1, _2)将会映射为:
f(a, b, _2, c, _1);

例如:调用g(x, y)会调用
f(a, b, y, c, x);

解析:
占位符的总个数即为new_calloable接受的参数的个数:用n(此处是2)表示;
因为bind中有两个占位符,因此,在调用bind生成的可调用对象时,要传递给它两个参数。

占位符数值i对应于new_callable中的第i个参数
对于bind中’_2’处于参数列表中第三个位置,因为是2,所以绑定到g(x, y)中的第二个参数;
因此,f中第三个位置的参数对应于g中的第2个参数;

2) 可以用bind重新安排其顺序
可以用bind颠倒compare的含义:

bool compare_1(const int a, const int b) {    return a < b;}//由短至长排序sort(v.begin(), v.end(), compare);//由长至短排序sort(v.begin(), v.end(), bind(compare, _2, _1));

绑定引用参数

默认情况下,bind绑定的那些 不是占位符的 参数被拷贝到bind返回的可调用对象中,但是与lamda类似,有时对绑定的参数希望用引用的方式传递,或者有些参数无法拷贝;

ex:

vector<string> v{"tx", "bd", "al", "ms", "ne"};ostream& os(cout);for_each(v.begin(), v.end(), [&os, c] (const string& s) {    os << s << c;});

可以很容易编写函数,完成相同的工作:

ostream& print(ostream& os, const string& s, char c) {    return os << s << c;}

但不能直接用bind代替对os的捕获;

//错误代码        for_each(v.begin(), v.end(), bind(print, os, _1, ' '));

因为bind拷贝其参数,而ostream类型无法拷贝。
如果我们希望传递给bind一个对象而又不拷贝,就必须使用标准库函数ref函数.

//正确代码for_each(v.begin(), v.end(), bind(print, ref(os), _1, ' '));

标准库还有一个cref函数,生成一个保存const引用的类。与bind一样,ref和cref都定义在头文件functional中。

向后兼容:参数绑定
STL源码剖析中提到的参数绑定时bind1st和bind2nd函数,类似bind,这两个函数接受一个函数作为参数生成一个新的可调用对象。但这些函数分别只能绑定第一个或者第二个参数。由于这些函数局限性太强,新标准中已经被废弃。因此,新的C++程序应该使用bind函数。

0 0