函数适配器: bind
2023年12月24日 2023年12月24日
bind
bind函数接受一个可调用对象及其参数列表
可调用对象的部分实参已在参数列表中给出
bind函数返回一个新的可调用对象, 调用该可调用对象时, 给出剩余实参, 达到调用给定可调用对象的效果
头文件
1#include <functional>
演示
- newCallable是一个可调用对象
- arg_list是一个逗号分隔的参数列表,与callable形参列表中的每一项相对应
arg_list由占位符_n
和实参组成, 占位符组成newCallable的形参列表 - 当我们调用newCallable时,传入newCallable所需的实参. 由newCallable调用callable, 并给出callable所需的实参
调用callable所需的实参, 一部分在我们定义newCallable时给出, 另一部分在调用newCallable时给出
1auto newCallable = bind(callable, arg_list);
示例
- 定义check6时, 给出check_size的第2个参数
6
(bind参数列表中在_1之后) - check_size的第一个参数在调用check6时给出, 作为check6的第一个参数
check6有且仅有一个形参: 只出现了编号为1的占位符_1
- 占位符
_1
作为check_size的第一个参数(bind参数列表中紧跟check_size给出), 类型为const string &
_n
的编号为1, 表明其是check6的第一个形参. 因此, check6的第一个形参类型为const string &
- 当我们调用check6(s)时, 相当于调用check_size(s, 6)
1bool check_size(const string &s, string::size_type sz) 2{ 3 return s.size() >= sz; 4} 5 6auto check6 = bind(check_size, _1, 6); 7 8check6(s);
命名空间
可以在程序中直接使用来自namespace_name的所有名字
1using namespace namespace_name;
占位符
形如 _n
. n是一个整数, 指示其为bind返回的可调用对象的第n个形参
如果bind返回的可调用对象有n个参数, 会使用占位符 _1
到 _n
; 它们出现的顺序不定, 也不要求相邻
在命名空间placeholders中
-
命令空间placeholders定义在头文件functional中
1#include <functional>
-
命名空间
placeholders
在命名空间std
中
如果需要使用占位符 _1
-
方法一
1using std::placeholders::_1;
-
方法二
1using namespace std; 2 3// 使用上条语句后,还得使用下面的 4using namespace std::placeholders;
使用占位符调整函数参数的顺序
g是一个有两个参数的可调用对象
1auto g = bind(f, a, b, _2, c, _1); 2 3g(p1, p2); 4 5// <=> 6// f(a, b, p2, c, p1);
使用占位符修改排序规则
1sort(words.begin(), words.end(), isShorter); 2// 按单词长度从小到大排序 3 4sort(words.begin(), words.end(), bind(isShorter, _2, _1); 5// 按单词长度从大到小排序
函数, lambda表达式和bind返回的可调用对象
- 函数作为谓词, 其最多拥有两个形参
- lambda表达式通过捕获列表, 增加了函数体内可以使用的参数
-
lambda表达式适合用来定义只在一两个地方使用的简单操作
如果需要在多个地方定义相同lambda表达式, 建议定义函数如果一个操作需要较多语句实现, 建议定义函数
尤其此时并不会用到捕获列表
-
对于捕获局部变量的lambda, 函数无法替换
check_size和lambda功能一样find_if只接受一元谓词, 此时只能使用lambda表达式
1bool check_size(const string &s, string::size_type sz) 2{ 3 return s.size() >= sz; 4} 5 6[sz](const string &a){ return a.size() >= sz; }
-
- bind返回的可调用对象填补了函数作为谓词的短板
-
find_if使用lambda作为谓词
1auto wc = find_if(words.begin(), words.end(), [sz](const string &a){ return a.size() >= sz; });
-
find_if使用bind返回的可调用对象作为谓词
bind调用返回一个可调用对象,将check_size的第二个参数绑定到sz的拷贝当find_if对words中的元素调用谓词时,谓词调用check_size,将元素和sz的拷贝作为参数传递
1bool check_size(const string &s, string::size_type sz) 2{ 3 return s.size() >= sz; 4} 5 6auto wc = find_if(words.begin(), words.end(), bind(check_size, _1, sz)); 7// find_if可以有效地对输入序列中每个元素调用check_size,实现元素长度与sz的比较
-
bind接受的参数列表中的实参, 在调用bind返回的可调用对象时, 传递的是实参的拷贝
调用bind返回的可调用对象时传入的实参, 作为原可调用对象的实参
1#include <iostream> 2#include <string> 3 4using namespace std; 5using namespace std::placeholders; 6 7void testString(string &s1, string &s2) 8{ 9 s1 = "hello"; 10 s2 = "world"; 11 cout << s1 << " " << s2 << endl; 12} 13 14int main() 15{ 16 string s1 = "good"; 17 string s2 = "morning"; 18 19 auto f = bind(testString, s1, _1); 20 f(s2); 21 22 cout << s1 << " " << s2 << endl; 23 24 testString(s1, s2); 25 26 cout << s1 << " " << s2 << endl; 27 28 return 0; 29}
输出如下
hello world good world hello world hello world
标准库: ref函数
使用bind时, 存在一些不支持拷贝的类类型; 当我们需要对参数执行写操作时, 需要传入引用
操作 | |
---|---|
ref | 返回一个引用, bind函数返回的可调用对象使用该引用作为实参 |
cref | ref的常量版本, 返回具有底层const的引用 |
头文件
1#include <functional>
示例
遍历元素时打印
- 使用lambda完成
1for_each(words.begin(), words.end(), [&os, c] (const string &s) { os << s << c; });
- 使用bind + ref
1ostream &print(ostream &os, const string &s, char c) 2{ 3 return os << s << c; 4} 5 6for_each(words.begin(), words.end(), bind(print, os, _1, ' ')); // 错误: ostream对象不支持拷贝 7 8for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));