18

我收到一个错误

错误:从“int”类型的右值对“int&”类型的非常量引用的初始化无效</p>

#include <thread>
#include <iostream>

using namespace std;

void func(int& i){
    cout<<++i<<endl;
}

int main(){
    int x=7;
    thread t(func,x);
    t.join();
    return 0;
}

我明白我做不到,thread(func, 4)但我x是一个变量,而不是暂时的。

我正在使用带有 -std=c++11 -pthread 的 gcc 4.7

为什么会出现这个错误?

4

2 回答 2

19

构造函数的规范std::thread

效果:构造一个线程类型的对象。新的执行线程执行INVOKE ( DECAY_COPY ( std::forward<F>(f)), DECAY_COPY 并在构造线程中评估对DECAY_COPY(std::forward<Args>(args))...)的调用。

其中DECAY_COPY(x)表示调用decay_copy(x)where 定义为:

template <class T> typename decay<T>::type decay_copy(T&& v)
{ return std::forward<T>(v); }

这意味着参数“衰减”并被复制,这意味着它们按值转发并失去任何 cv 限定。因为线程运行的目标函数想要通过引用获取其参数,所以您会收到一个编译器错误,指出引用无法绑定到按值传递的对象。

这是设计使然,因此默认情况下,局部变量通过std::thread值传递(即复制)而不是通过引用传递给 get,因此新线程不会对超出范围的局部变量产生悬空引用,从而导致未定义的行为。

如果您知道通过引用传递变量是安全的,那么您需要明确地这样做,使用reference_wrapper不受“衰减”语义影响的 a ,并将通过引用将变量转发给目标对象。您可以创建一个reference_wrapper使用std::ref.

于 2013-03-06T00:20:35.393 回答
16

xstd::ref创建线程时包裹。

如果每次创建时std::thread它都通过引用获取所有变量,想一想会发生什么:如果您传入一个堆栈局部变量,它将是一个悬空引用,如果线程超出自动存储​​的范围,则会导致未定义的行为多变的。这在实践中会发生很多,并导致许多错误。相反,std::thread默认情况下(通过完美转发编组)所有参数(包括变量)按值。

可以通过传入 的线程本地左值副本调用您的std::future工作函数时静默工作x,但这会非常令人困惑:工作任务将编辑x您认为您通过引用传递的内容,并且它不会显示在x外部的任务。相反,它有助于为您提供该错误消息。你应该感谢你的幸运星!

为了表明你真的不想按值取值,你将它包装在 中std::ref,现在它作为引用一直传递给工作函数。在这种情况下,您负责管理引用的生命周期,以便引用的数据至少持续到std::future的工作任务需要它的时间。

于 2013-03-05T23:11:45.720 回答