六一的部落格


关关难过关关过,前路漫漫亦灿灿。



bind

bind函数接受一个可调用对象及其参数列表

可调用对象的部分实参已在参数列表中给出

bind函数返回一个新的可调用对象, 调用该可调用对象时, 给出剩余实参, 达到调用给定可调用对象的效果


头文件

1#include <functional>

演示

  1. newCallable是一个可调用对象
  2. arg_list是一个逗号分隔的参数列表,与callable形参列表中的每一项相对应

    arg_list由占位符 _n 和实参组成, 占位符组成newCallable的形参列表
  3. 当我们调用newCallable时,传入newCallable所需的实参. 由newCallable调用callable, 并给出callable所需的实参

调用callable所需的实参, 一部分在我们定义newCallable时给出, 另一部分在调用newCallable时给出

1auto newCallable = bind(callable, arg_list);

示例

  1. 定义check6时, 给出check_size的第2个参数 6 (bind参数列表中在_1之后)
  2. check_size的第一个参数在调用check6时给出, 作为check6的第一个参数

    check6有且仅有一个形参: 只出现了编号为1的占位符 _1
  3. 占位符 _1 作为check_size的第一个参数(bind参数列表中紧跟check_size给出), 类型为 const string &

    _n 的编号为1, 表明其是check6的第一个形参. 因此, check6的第一个形参类型为 const string &
  4. 当我们调用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中

  1. 命令空间placeholders定义在头文件functional中

    1#include <functional>
  2. 命名空间 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返回的可调用对象

  1. 函数作为谓词, 其最多拥有两个形参
  2. 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; }
  3. 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>

示例

遍历元素时打印

  1. 使用lambda完成
    1for_each(words.begin(), words.end(), [&os, c] (const string &s) { os << s << c; });
  2. 使用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, ' '));

函数适配器: bind


bind

bind函数接受一个可调用对象及其参数列表

可调用对象的部分实参已在参数列表中给出

bind函数返回一个新的可调用对象, 调用该可调用对象时, 给出剩余实参, 达到调用给定可调用对象的效果


头文件

1#include <functional>

演示

  1. newCallable是一个可调用对象
  2. arg_list是一个逗号分隔的参数列表,与callable形参列表中的每一项相对应

    arg_list由占位符 _n 和实参组成, 占位符组成newCallable的形参列表
  3. 当我们调用newCallable时,传入newCallable所需的实参. 由newCallable调用callable, 并给出callable所需的实参

调用callable所需的实参, 一部分在我们定义newCallable时给出, 另一部分在调用newCallable时给出

1auto newCallable = bind(callable, arg_list);

示例

  1. 定义check6时, 给出check_size的第2个参数 6 (bind参数列表中在_1之后)
  2. check_size的第一个参数在调用check6时给出, 作为check6的第一个参数

    check6有且仅有一个形参: 只出现了编号为1的占位符 _1
  3. 占位符 _1 作为check_size的第一个参数(bind参数列表中紧跟check_size给出), 类型为 const string &

    _n 的编号为1, 表明其是check6的第一个形参. 因此, check6的第一个形参类型为 const string &
  4. 当我们调用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中

  1. 命令空间placeholders定义在头文件functional中

    1#include <functional>
  2. 命名空间 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返回的可调用对象

  1. 函数作为谓词, 其最多拥有两个形参
  2. 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; }
  3. 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>

示例

遍历元素时打印

  1. 使用lambda完成
    1for_each(words.begin(), words.end(), [&os, c] (const string &s) { os << s << c; });
  2. 使用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, ' '));