我使用以下最小示例来重现我在尝试创建一个调用非静态成员函数的线程来完成其工作时得到的编译器错误:
#include <thread>
#include <iostream>
class Worker
{
public:
Worker() : m_worker(&Worker::doWork, std::ref(*this), 1)
{}
std::thread m_worker;
void doWork(int a) { std::cout << a << std::endl; }
};
int main(int argc, char* argv[]) {
Worker k;
}
使用gcc4.8-gcc5.1时,无法编译,原因如下:
In file included from /usr/include/c++/4.8/thread:39:0,
from /tmp/gcc-explorer-compiler115614-69-rgangs/example.cpp:1:
/usr/include/c++/4.8/functional: In instantiation of 'struct std::_Bind_simple<std::_Mem_fn<void (Worker::*)(int)>(std::reference_wrapper<Worker>, int)>':
/usr/include/c++/4.8/thread:137:47: required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (Worker::*)(int); _Args = {std::reference_wrapper<Worker>, int}]'
7 : required from here
/usr/include/c++/4.8/functional:1697:61: error: no type named 'type' in 'class std::result_of<std::_Mem_fn<void (Worker::*)(int)>(std::reference_wrapper<Worker>, int)>'
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/4.8/functional:1727:9: error: no type named 'type' in 'class std::result_of<std::_Mem_fn<void (Worker::*)(int)>(std::reference_wrapper<Worker>, int)>'
_M_invoke(_Index_tuple<_Indices...>)
^
Compilation failed
另一方面,Clang似乎很好地编译了这段代码。谁在这里是正确的,这是gcc中的错误(打开门票?)?
编辑:使用m_worker(&Worker::doWork, this, 1)
初始化线程时,gcc编译就好了。那么,在这种情况下使用std::ref(*this)
是否合法?我猜任何std::ref()
都更为一般。
答案 0 :(得分:4)
您的thread
构造函数调用依赖于以下语义:
[C++14: 30.3.1.2/3]:
要求:F
且Ti
中的每个Args
都应满足 MoveConstructible 要求。 INVOKE(
DECAY_COPY( std::forward<F>(f)),
DECAY_COPY(std::forward<Args>(args))...)
(20.9.2)应为有效表达式。
因此定义了INVOKE
:
[C++14: 20.9.2/1]:
按如下方式定义 INVOKE(f, t1, t2, ..., tN)
:
- 派生的类型的对象的引用
(t1.*f)(t2, ..., tN)
当f
是指向类T
的成员函数的指针时,t1
是T
类型的对象或对{类型为T
的对象或对从T
;((*t1).*f)(t2, ..., tN)
当f
是指向类T
的成员函数的指针时,t1
不是上一项中描述的类型之一;t1.*f
当N == 1
和f
是指向类T
的成员数据的指针时,t1
是T
类型的对象或对T
类型的对象的引用或对T
派生类型的对象的引用;- 在所有其他情况下
(*t1).*f
当N == 1
和f
是指向类T
的成员数据的指针时,t1
不是之前描述的类型之一项;f(t1, t2, ..., tN)
。
正如您所看到的,此处没有std::reference_wrapper<Worker>
的规定,这是std::ref(*this)
为您提供的内容。当然,衰变规则中的任何内容都无济于事([C++14: 30.2.6/1]
)。
Clang实际上是在这里稍稍跳了一下,似乎允许这样做,因为有一天我们会自己Jonathan Wakely申请Library Working Group issue #2219,这将符合标准。但是,就目前而言,它不是。
无论如何,这一切都没有实际意义。无需编写此代码。写下这个:
Worker() : m_worker(&Worker::doWork, this, 1)