首页 文章

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

提问于
浏览
16

我收到一个错误

错误:从'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;
}

我明白我不能做 thread(func, 4) 但是 x 是一个变量,而不是一个临时变量 .

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

为什么会出现此错误?

2 回答

  • 15

    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); }
    

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

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

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

  • 18

    创建线程时在 std::ref 中包装 x .

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

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

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

相关问题