【C++编程】function、bind

 bind用法(10)

占位符 表示
_1 调用时的第1个参数
_2 调用时的第2个参数
_3 第3个参数
... ...

 

#include <iostream>
#include <functional>

void print(int a, int b) {
    std::cout << a << " " << b << std::endl;
}

int main() {
    auto f = std::bind(print, 10, std::placeholders::_1);
    f(20);   // 输出:10 20
}

解释:std::bind(print, 10, _1)  等价于 f(x) => print(10, x)

 

参数重排:

auto f = std::bind(print,
                   std::placeholders::_2,
                   std::placeholders::_1);

f(1, 2);   // 输出:2 1

 

1. 绑定一个普通函数

#include <iostream>
#include <functional>

void print(int a, int b) {
    std::cout << a << " " << b << std::endl;
}

int main() {
    std::function<void(int)> f =
        std::bind(print, 100, std::placeholders::_1);

    f(200);   // 100 200
}

 

std::bind绑定一个成员函数

#include <iostream>
#include <functional>

struct Foo {
    void print_sum(int n1, int n2) {
        std::cout << n1 + n2 << '\n';
    }
    int data = 10;
};

int main() {
    Foo foo;
    auto f1 = std::bind(&Foo::print_sum, &foo, 95, std::placeholders::_1);
    f1(5); // 100

    std::function<void(int)> f1 = std::bind(&Foo::print_sum, &foo, 95, std::placeholders::_1);
    f2(5); //100
    return 0;
}

  

 1 #include <iostream>
 2 #include <functional>
 3 
 4 struct Foo 
 5 {
 6     void print_sum(int n1, int n2)
 7     {
 8         std::cout << n1 + n2 << '\n';
 9     }
10     int data = 10;
11 };
12 int main()
13 {
14     Foo foo;
15     auto f = std::bind(&Foo::print_sum, &foo, 95, std::placeholders::_1);
16     f(5); // 100
17 }
  • bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。
  • 必须显示的指定&Foo::print_sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Foo::print_sum前添加&;
  • 使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &foo;

绑定成员变量:

class Test {
public:
    int value = 42;
};

Test t;

auto f = std::bind(&Test::value, t);
std::cout << f() << std::endl;  // 42

3.3 绑定一个引用参数

默认情况下,bind 会拷贝参数。

  • std::bind使用的是参数的拷贝而不是引用,当可调用对象期待入参为引用时,必须显示利用std::ref来进行引用绑定
  • 多线程std::thread的可调用对象期望入参为引用时,也必须显式通过std::ref来绑定引用进行传参

如果你想传引用,必须使用:

std::ref(obj)
std::cref(obj)

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

 

void modify(int& x) {
    x = 100;
}

int main() {
    int a = 10;

    auto f = std::bind(modify, std::ref(a));
    f();

    std::cout << a << std::endl;  // 100
}
 1 #include <iostream>
 2 #include <functional>
 3 #include <vector>
 4 #include <algorithm>
 5 #include <sstream>
 6 using namespace std::placeholders;
 7 using namespace std;
 8 
 9 ostream& print(ostream &os, const string& s, char c)
10 {
11     os << s << c;
12     return os;
13 }
14 
15 int main()
16 {
17     vector<string> words{ "helo", "world", "this", "is", "C++11" };
18     ostringstream os;
19     char c = ' ';
20     for_each(words.begin(), words.end(),
21         [&os, c](const string & s) {os << s << c; });
22     cout << os.str() << endl;
23 
24     ostringstream os1;
25     // ostream不能拷贝,若希望传递给bind一个对象,
26     // 而不拷贝它,就必须使用标准库提供的ref函数
27     for_each(words.begin(), words.end(),
28         bind(print, ref(os1), _1, c));
29     cout << os1.str() << endl;
30 }

例4:

 1 #include <iostream>
 2 #include <functional>
 3 using namespace std;
 4 
 5 class Computer
 6 {
 7 public:
 8     static int Add(int i, int j) { return i + j; }
 9 
10     template <class T>
11     static T AddT(T i, T j) { return i + j; }
12 
13     int AddN(int i, int j) { return i + j; }
14 };
15 
16 int main()
17 {
18     //1、 类静态函数
19     function<int(int, int)> f = &Computer::Add;
20     cout << f(1, 1) << endl;
21 
22     //2、 类静态模板函数
23     function<int(int, int)> ft = &Computer::AddT<int>;
24     cout << ft(1, 1) << endl;
25 
26     //普通函数绑定  需要构造类对象
27     Computer c;
28     //3、 普通函数 需使用bind,将类对象地址 &c 绑定上
29     function<int(int, int)> fN = bind(&Computer::AddN, &c, placeholders::_1, placeholders::_2);
30     cout << fN(1, 1) << endl;
31 
32     //4、普通函数, 也可以这样调用  个人觉得这个比 bind 麻烦,不建议
33     function<int(const Computer &, int, int)> fN2 = &Computer::AddN;
34     cout << fN2(c, 1, 1) << endl;
35     return 0;
36 }
View Code

 

ref 与 cref 的用法

bind()是一个函数模板,它的原理是根据已有的模板,生成一个函数,但是由于bind()不知道生成的函数执行的时候,传递进来的参数是否还有效。所以它选择参数值传递而不是引用传递。如果想引用传递,std::ref和std::cref就派上用场了。

  • std::ref 用于包装按引用传递的值。
  • std::cref 用于包装按const引用传递的值。
 1 #include <functional>
 2 #include <iostream>
 3 
 4 void f(int &n1, int &n2, const int &n3)
 5 {
 6   std::cout << "In function: n1[" << n1 << "]    n2[" << n2 << "]    n3[" << n3 << "]" << std::endl;
 7   ++n1; // 增加存储于函数对象的 n1 副本
 8   ++n2; // 增加 main() 的 n2
 9   //++n3; // 编译错误
10   std::cout << "In function end: n1[" << n1 << "]     n2[" << n2 << "]     n3[" << n3 << "]" << std::endl;
11 }
12 
13 int main()
14 {
15   int n1 = 1, n2 = 1, n3 = 1;
16   std::cout << "Before function: n1[" << n1 << "]     n2[" << n2 << "]     n3[" << n3 << "]" << std::endl;
17   std::function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3));
18   bound_f();
19   std::cout << "After function: n1[" << n1 << "]     n2[" << n2 << "]     n3[" << n3 << "]" << std::endl;
20 }
View Code

输出结果:

 

  • std::bind使用的是参数的拷贝而不是引用,当可调用对象期待入参为引用时,必须显示利用std::ref来进行引用绑定
  • 多线程std::thread的可调用对象期望入参为引用时,也必须显式通过std::ref来绑定引用进行传参

 

参考资料

 

现在C++用法

bind写法:

std::function<void(int)> f = std::bind(print, 10, std::placeholders::_1);

lambda写法:

std::function<void(int)> f =
    [=](int x) {
        print(10, x);
    };

 

1. std::bind 的用法

 

posted @ 2019-03-20 20:44  苏格拉底的落泪  阅读(558)  评论(0)    收藏  举报