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);
//...
}
但是我对最近的个人发现很感兴趣:std::bind
允许你在上面的案例中传递一个值,即使这不是人们通常想要的。
std::bind(f, x, _1) (3.14); // Usually wrong, but valid.
然而,std::thread
并非如此。以下内容将触发编译错误。
std::thread t2(f, x, 3.14); // Usually wrong and invalid: Error!
乍一看,我认为这是一个编译器错误,但错误确实合法。由于copy decaying 要求(std::thread
转换int&
,int
的构造函数的模板化版本似乎无法正确推断出参数。由30.3.1.2强加的。
问题是:为什么不要求类似于std::bind
的论点?或者这种明显的不一致是什么意思?
注意:解释为什么它在下面的评论中不重复。
答案 0 :(得分:6)
std::bind
在到达时大多已经过时了。使用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';
}
打印
1
2
3
0
最后0
是原始x
,而1
2
和3
是x
中增加的f
副本。 {1}}。
使用lambda,可以明确可变存储状态和外部引用之间的差异。
auto b = [&x]{ f(x); };
VS
auto b = [x]()mutable{ f(x); };
其中一个副本x
然后重复调用f
,另一个副本将x
的引用传递给f
。
bind
实际上没有办法在f
允许x
访问std::thread
的存储副本作为参考的情况下执行此操作。
对于std::thread t1([x]()mutable{ f(x); });
,如果你想要这个可变的本地拷贝行为,你只需要使用lambda。
std::apply
事实上,我认为C ++ 11中的大多数INVOKE语法似乎都是在语言中没有C ++ 14幂lambdas和std::apply
的遗产。很少有案例没有通过lambda和John's iPad <actual unique ID from the server>
John's iPad <actual unique ID from the server>
John's iPad <actual unique ID from the server>
John's iPad <actual unique ID from the server>
John's iPad <actual unique ID from the server>
解决(需要应用,因为lambda不能轻易地支持将包移入其中然后在里面进行处理)。
但是我们没有时间机器,所以我们有这些多种并行方式来表达在C ++中调用特定上下文中的内容的想法。
答案 1 :(得分:6)
据我所知,thread
的基本规则与bind
基本相同,但在2010年由N3090修改,以接受您已识别的约束。< / p>
使用它来平分各种贡献,我相信你正在寻找LWG issue 929。
具有讽刺意味的是,其目的似乎是使thread
构造函数较少受到限制。当然没有提及bind
,虽然this wording was later also applied to async
(LWG 1315之后的“清理”部分),所以我会说bind
被遗忘。
但是很难确定,所以我建议问the committee itself。