std::bind并std::thread分享一些设计原则.由于它们都存储了与传递的参数相对应的本地对象,因此我们需要使用std::ref或者std::cref如果需要引用语义:
void f(int& i, double d) { /*...*/ }
void g() {
int x = 10;
std::bind(f, std::ref(x), _1) (3.14);
std::thread t1(f, std::ref(x), 3.14);
//...
}
Run Code Online (Sandbox Code Playgroud)
但我对最近的个人发现很感兴趣:std::bind即使这不是人们通常想要的,也会允许你在上面的案例中传递一个值.
std::bind(f, x, _1) (3.14); // Usually wrong, but valid.
Run Code Online (Sandbox Code Playgroud)
但是,事实并非如此std::thread.以下将触发编译错误.
std::thread t2(f, x, 3.14); // Usually wrong and invalid: Error!
Run Code Online (Sandbox Code Playgroud)
乍一看,我认为这是一个编译器错误,但错误确实合法.看来的模板版本std::thread的构造函数是无法正确推断的参数,由于副本腐烂 要求(tranforming int&在int通过30.3.1.2规定).
问题是:为什么不要求类似于std::bind论点的东西?或者这是否明显不一致?
注意:解释为什么它在下面的评论中不重复.
返回的函数对象bind是为重用而设计的(即调用被多次调用); 因此,它必须将其绑定参数作为左值传递,因为您不希望从所述参数移动或稍后调用将看到移动的绑定参数.(同样,您也希望将函数对象作为左值调用.)
这种担忧不适用于std::thread朋友.线程函数只能使用提供的参数调用一次.离开它们是完全安全的,因为没有别的东西可以看着它们.它们实际上是临时副本,仅用于新线程.因此,函数对象被称为rvalue,参数作为rvalues传递.
std::bind由于lambda的存在而到达时,它大部分已经过时了.随着C++ 14的改进和C++ 17 std::apply,剩下的用例bind几乎消失了.
即使在C++ 11中,bind解决了lambda没有解决的问题的情况相对较少.
另一方面,std::thread解决了一个稍微不同的问题.它不需要bind"解决每个问题" 的灵活性,而是可以阻止通常是坏代码.
在这种bind情况下,传递给的引用f不会是x对内部存储副本的引用x.这非常令人惊讶.
void f(int& x) {
++x;
std::cout << x << '\n';
};
int main() {
int x = 0;
auto b = std::bind(f, x);
b();
b();
b();
std::cout << x << '\n';
}
Run Code Online (Sandbox Code Playgroud)
版画
1
2
3
0
Run Code Online (Sandbox Code Playgroud)
其中最后一个0是原来的x,而1 2和3是递增副本x内存储f.
使用lambda,可以明确可变存储状态和外部引用之间的差异.
auto b = [&x]{ f(x); };
Run Code Online (Sandbox Code Playgroud)
VS
auto b = [x]()mutable{ f(x); };
Run Code Online (Sandbox Code Playgroud)
其中一个副本x然后f重复调用它,另一个副本传递给xinto f.
bind如果不允许f访问存储的副本x作为参考,真的没有办法做到这一点.
因为std::thread,如果你想要这个可变的本地拷贝行为,你只需要使用lambda.
std::thread t1([x]()mutable{ f(x); });
Run Code Online (Sandbox Code Playgroud)
事实上,我认为C++ 11中的大多数INVOKE语法似乎都是没有C++ 14权力lambda和std::apply语言的遗产.很少有案例没有通过lambda解决std::apply(并且需要应用,因为lambda不能轻易地支持移动包进入它们然后在里面取出它们).
但是我们没有时间机器,所以我们有这些多种并行方式来表达在C++中在特定上下文中调用某些东西的想法.
据我所知,thread从基本相同的规则开始bind,但是在2010年被N3090修改为接受你已经确定的约束.
使用它来平分各种贡献,我相信你正在寻找LWG问题929.
具有讽刺意味的是,目的似乎是使thread构造函数受到较少限制.当然没有提及bind,虽然这个措辞后来也适用于async(LWG 1315之后的"清理"部分),所以我会说bind落后了.
但是,很难确定,所以我建议问委员会本身.