这是一个例子:
#include<iostream>
#include<thread>
using namespace std;
void f1(double& ret) {
ret=5.;
}
void f2(double* ret) {
*ret=5.;
}
int main() {
double ret=0.;
thread t1(f1, ret);
t1.join();
cout << "ret=" << ret << endl;
thread t2(f2, &ret);
t2.join();
cout << "ret=" << ret << endl;
}
输出是:
ret=0
ret=5
使用gcc 4.5.2编译,带有和不带-O2
标记。
这是预期的行为吗?
这个节目数据是否免费比赛?
谢谢
答案 0 :(得分:83)
std::thread
的构造函数推导出参数类型并按值存储它们的副本。这需要确保参数对象的生命周期至少与线程的生命周期相同。
C ++模板函数参数类型推导机制从类型T
的参数中推导出类型T&
。将复制std::thread
的所有参数,然后将其传递给线程函数,以便f1()
和f2()
始终使用该副本。
如果您坚持使用引用,请使用boost::ref()
或std::ref()
包装参数:
thread t1(f1, boost::ref(ret));
或者,如果您更喜欢简单,请传递指针。这是boost::ref()
或std::ref()
在幕后为您做的事情。
答案 1 :(得分:9)
在这些情况下,您需要明确的std::ref()
(或boost::ref()
)实际上是一个非常有用的安全功能,因为传递引用本质上是一件危险的事情。
使用非const引用时,通常存在传递局部变量的危险,使用const引用它可能是临时的,并且正在创建要在不同线程中调用的函数(并且通常使用绑定,通常是稍后/以异步方式调用的函数),您将面临对象不再有效的巨大危险。
绑定看起来很整洁,但这些错误是最难找到的,因为错误被捕获(即在调用函数时)与错误的位置不同(在绑定时)并且它可能非常很难确切地确定当时正在调用哪个函数,因此它被绑定的位置。
在您作为参考传递的变量范围内加入线程时,在您的实例中是安全的。因此,当您知道这种情况时,就会有一种传递引用的机制。
这不是我想要改变的语言的一个特征,特别是因为可能有很多现有的代码依赖于它制作一个副本,如果它只是自动引用就会破坏(然后需要一个强制复制的明确方法。
答案 2 :(得分:7)