采用引用参数的 C++ 线程编译失败
Posted
技术标签:
【中文标题】采用引用参数的 C++ 线程编译失败【英文标题】:C++ Thread taking reference argument failed compile 【发布时间】:2016-03-31 19:10: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;
以上代码编译失败,error message:
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
应该只通过 value 复制参数并传递存储在线程内的一些对象以与函数 f1
的引用参数绑定。
我觉得如果我能理解这个result_of
在做什么以及为什么会出错,我就能更好地理解原因。那么任何人都可以引导我完成错误消息吗?特别是std::_Bind_simple<void (*(double))(double&)>
和std::result_of<void (*(double))(double&)>
的含义。
EDIT:我知道如果我传递一个值,线程只会在副本上工作,线程返回后没有效果。这不是我关心的问题。我想知道为什么它现在给出错误,但它没有给 SO 上的其他帖子给出错误,如下所示:Difference between pointer and reference as thread parameter
【问题讨论】:
@Barry,您的编辑丢失了“突出显示的部分”。 @JonathanWakely 解决了这个问题。 【参考方案1】:我知道如果我传递了一个值,线程只会在副本上工作,线程返回后没有效果。
不,这是不正确的。代码不应该默默地复制并在副本上工作,标准规定它甚至不能编译。
标准要求将被调用函数的参数复制(到由 C++ 运行时管理的存储中),然后将副本作为右值转发。因此,在您的示例中,f1
传递了一个 double
类型的右值,而 double&
类型的参数无法绑定到该右值。
标准要求这样做的原因是没有静默复制和数据丢失:如果函数需要可修改的引用,那么除非您使用 reference_wrapper
显式传递引用,否则它将无法编译。
您得到的编译器错误涉及result_of
,因为这就是我使 GCC 的std::thread
检查是否可以使用提供的参数调用函数的方式。我使用result_of<decltype(&f1)(double)>
来检查是否可以使用double
类型的右值调用函数指针&f1
(类型为void(*)(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 标准要求的不符合要求的编译器,并且错误地编译了代码。
【讨论】:
来自en.cppreference.com/w/cpp/thread/thread/thread,它明确表示会将参数复制/移动到线程可访问存储 那又怎样?这并不意味着它应该编译。确保您已刷新我的答案以查看最新版本,该版本解释了副本作为右值转发,因此无法绑定到非 const 左值引用参数。 您能详细说明std::result_of
错误吗?是按照您所说的那样尝试执行标准要求的操作,即将副本作为右值传递吗?
result_of 错误意味着您不能使用double
类型的参数调用void(double&)
类型的函数,这是真的,因为您不能将非常量左值引用绑定到右值。所以你不能用这些参数构造一个std::thread
,因为用右值调用f1
不会编译。
我有点困惑。为什么double
的参数类型是右值?【参考方案2】:
乔纳森的回答是肯定的。花时间研究它是值得的。
与此同时,修改代码将做你想做的事 - 即将引用发送到线程函数:
thread t1(f1, std::ref(ret));
【讨论】:
以上是关于采用引用参数的 C++ 线程编译失败的主要内容,如果未能解决你的问题,请参考以下文章