使用C++ 11线程初始化非const引用无效?

Ale*_*ene 18 c++ c++11

我收到一个错误

错误:从'int'类型的右值开始,无效初始化'int&'类型的非const引用

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

我正在使用gcc 4.7和-std = c ++ 11 -pthread

为什么会出现此错误?

Jon*_*ely 19

std::thread构造函数的规范说

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

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

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

这意味着参数"衰变"并被复制,这意味着它们按价值转发并失去任何cv资格.因为线程运行的目标函数想要通过引用获取其参数,所以会得到编译器错误,指出引用不能绑定到由value传递的对象.

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

如果您知道通过引用传递变量是安全的,那么您需要使用reference_wrapper不会受"衰减"语义影响的显式,并且将通过引用转发目标对象来转发变量.您可以创建一个reference_wrapper使用std::ref.

  • 我真的很想知道为什么标准没有给我们`std :: invoke`和`std :: decay_copy` ... (2认同)
  • 我也遇到了这个问题,我想补充一点,如果目标函数的参数是const int&或int &&,它就会起作用.所以我认为non-const ref不起作用的原因是因为rvalue被传递给它. (2认同)
  • @szli,是的,这正是编译器错误所说的!_"来自'int'类型的右值"_.要理解的是,目标函数传递参数的_copy_是一个rvalue_. (2认同)

Yak*_*ont 16

xstd::ref创建线程时包装.

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

std::future通过传入线程局部左值副本来调用你的worker函数时,可以默默地工作x,但这会很混乱:工作者任务会编辑x你认为你通过引用传递,并且它不会出现在x外面任务.相反,它有助于您提供该错误消息.你应该感谢你的幸运星!

为了表明你真的想要不按值取值,你将它包装进去std::ref,现在它一直作为参考传递给worker函数.在这种情况下,您负责管理引用的生命周期,以便引用的数据至少持续std::future与工作者任务需要它一样长.

  • 这将解决错误,但问题是为什么它是一个错误. (7认同)
  • @juanchopanza是的 - 我在手机上,所以我快速修复了Alex的问题.细节现已添加. (2认同)