bind函数
根据一个可调用对象,生成一个新的可调用对象
int Add (int a, int b)
{
return a+b;
}
auto NewAdd = std::bind(Add, std::placeholders::_1, 4);
int res = NewAdd(3);//-->Add(3,4)
上面这段代码可以理解为,利用Add函数生成一个新的函数NewAdd,NewAdd的参数有一个,会在调用Add时候把参数放到_1的占位符的地方。
占位符
placeholders ,占位符。表示新的函数对象中参数的位置。当调用新的函数对象时,新函数对象会调用被调用函数,并且其参数会传递到被调用函数参数列表中持有与新函数对象中位置对应的占位符。
void function(arg1,arg2,arg3,arg4,arg5){};
auto g = bind(function,a,b,_2,c,_1);
//新的函数对象:g
//被调用函数:function
当调用函数对象g时候,函数对象g会调用function函数,并把其参数传给function函数,g的第一个参数会传给function的持有占位符_1的位置,即arg5。第二个参数会传给function的持有占位符_2的位置,即arg3。 其中的arg1,arg2,arg4已经被绑定到a,b,c上。
void g(X,Y);
//相对于调用下面函数
function(function,a,b,Y,c,X);
问题
假如是这样的:
#include <iostream>
#include <functional>
int func(int& a, int& b)
{
a = b + a;
b = a + b;
return a;
}
int main()
{
int a = 3;
int b = 4;
auto foo = bind(func, a, std::placeholders::_1);
std::cout<<foo(b)<<std::endl;
std::cout<<a<<std::endl;
std::cout<<b<<std::endl;
}
//out
7
3
11
可以看到生成的新函数并没有更新a的结果,为什么?因为bind函数是参数是传值的方式,也就是会拷贝一个临时变量,这样传入的就是一个临时变量,是一个右值(不过为啥运行没报错就不明白了),所以a的值没有更新,而placeholders其实是一个引用,也就是传入的是个指针
,所以会改变。问题来了,如何解决?
- foo = bind(func, std::placeholders::_1, std::placeholders::_2); 每次调用时候这样写foo(a, x)就行
- auto foo = bind(func, std::ref(a), std::placeholders::_1);
其实这个std::ref比较好理解,唯一要理解的就是,bind和thread都是尼玛模板函数,所以当你传入引用的时候,就会调用引用构造函数,当bind传入的是一个引用类型,那么就会调用引用构造函数,所以底层接受的其实变成了指针而不再是临时对象了。在thread对象的初始化时候也会有同样的问题,解决方案一样。
bind() 和 thread()原理
// thread的构造函数申明
template<class Function, class... Args>
explict thread(Function&& f, Args&&... args);
// bind的构造函数申明
template<class Function, class... Args>
bind(Function&& f, Args&&... args);
可以看出来,基本申明是一模一样的,所以表现得结果也是一样,但是其实还是有区别的,上述代码只是引用的值没变,但是可以编译成功,对于thread会直接编译失败,可以参考下面的代码:
#include <iostream>
#include <thread>
void thread1(int b)
{
std::cout << "do something in thread1" << std::endl;
}
void thread2(int &b)
{
std::cout << "do something in thread2" << std::endl;
}
int main()
{
int a = 10;
std::thread t1(thread1, a);
t1.join();
// std::thread t2(thread2, a); 这里编译错误!!!
// t2.join();
}
面对上面问题有两种解决方式,一种是函数定义时,形参的引用改成const 引用,第二种是用std::ref(a)包一下再传给thread的构造函数。
对于线程构造而言,会首先把所有实参拷贝到自己的内存区间,然后把右值传给函数使用,这里有个问题就是假如形参是左值引用类型就会失败。这里bind()函数好像没有。
这里有个奇怪的玩法,就是std::ref, 下面的代码可以编译通过,后续看看原理:
int e =10;
int& f = std::move(std::ref(e)); //可以编译成功
int& g = 30; //编译失败