11
#include<iostream>
#include<thread>
using namespace std;

void f1(double& ret) {
   ret=5.;
}

int main() {
   double ret=0.;
   thread t1(f1, ret);
   t1.join();
   cout << "ret=" << ret << endl;
}

上面的代码编译失败并出现以下错误消息

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
In file included from /usr/local/include/c++/5.3.0/thread:39:0,
                 from main.cpp:2:
/usr/local/include/c++/5.3.0/functional: In instantiation of 'struct std::_Bind_simple<void (*(double))(double&)>':
/usr/local/include/c++/5.3.0/thread:137:59:   required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(double&); _Args = {double&}]'
main.cpp:11:21:   required from here
/usr/local/include/c++/5.3.0/functional:1505:61: error: no type named 'type' in 'class std::result_of<void (*(double))(double&)>'
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/local/include/c++/5.3.0/functional:1526:9: error: no type named 'type' in 'class std::result_of<void (*(double))(double&)>'
         _M_invoke(_Index_tuple<_Indices...>)
         ^

我知道我可以std::ref()用来传递论点。但是,如果我按值传递,为什么会出现错误,因为应该只按thread复制参数并传递存储在线程内的一些对象以与 function 的引用参数绑定。f1

我觉得如果我能理解这result_of是在做什么以及为什么会出错,我就能更好地理解原因。那么任何人都可以引导我完成错误消息吗?尤其是std::_Bind_simple<void (*(double))(double&)>和的含义std::result_of<void (*(double))(double&)>

编辑:我知道如果我传递一个值,线程只会在副本上工作,并且在线程返回后没有效果。这不是我关心的问题。我想知道为什么它现在给出错误,但它没有给 SO 上的其他帖子给出错误,如下所示:指针和引用之间的差异作为线程参数

4

2 回答 2

15

我知道如果我传递一个值,线程只会在副本上工作,并且在线程返回后没有效果。

不,这是不正确的。代码不应该默默地复制并在副本上工作,标准规定它甚至不能编译。

该标准要求将被调用函数的参数复制(到由 C++ 运行时管理的存储中),然后将副本作为 rvalues 转发。因此,在您的示例中f1,传递了一个类型的右值,double并且类型的参数double&不能绑定到该右值。

标准要求这样做的原因是没有静默复制和数据丢失:如果函数需要可修改的引用,那么除非您使用reference_wrapper.

您得到的编译器错误涉及到result_of,因为这就是我让 GCCstd::thread检查是否可以使用提供的参数调用该函数的方式。我result_of<decltype(&f1)(double)>用来检查函数指针&f1(它是 type void(*)(double&))是否可以用 type 的右值调用double。不能使用该类型的参数调用它,因此result_of<decltype(&f1)(double)>::type未定义嵌套类型,因此编译器说:

error: no type named 'type' in 'class std::result_of<void (*(double))(double&)>'

该错误有点令人困惑,因为 C++ 声明器规则意味着decltype(&f1)(double)显示为void(*(double))(double&).

这不是我关心的问题。我想知道为什么它现在会出错,但它没有给 SO 上的其他帖子出错

这些帖子使用的是旧的 C++11 之前的编译器或不符合 C++11 标准要求的不符合标准的编译器,并且错误地编译了代码。

于 2016-03-31T19:19:52.567 回答
3

乔纳森的回答是确定的。把时间花在学习上是值得的。

与此同时,修改代码将做你想做的事——即将引用发送到线程函数:

thread t1(f1, std::ref(ret));
于 2016-03-31T20:19:49.820 回答