Reputation: 5044
I use the following minimal example to reproduce the compiler error I get when I try to make a thread that calls a non-static-member function to do its work:
#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;
}
When using gcc4.8-gcc5.1 this fails to compile with the following reason:
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, on the other hand seems to compile this code just fine. Who is correct here and is this a bug in gcc (with open tickets?)?
EDIT: When using m_worker(&Worker::doWork, this, 1)
to initialize the thread, gcc compiles this just fine. So, is it legal to use std::ref(*this)
in this case? I guess any std::ref()
, to be more general.
Upvotes: 1
Views: 1987
Reputation: 385204
Your thread
constructor call relies on the following semantics:
[C++14: 30.3.1.2/3]:
Requires:F
and eachTi
inArgs
shall satisfy the MoveConstructible requirements. INVOKE(
DECAY_COPY( std::forward<F>(f)),
DECAY_COPY(std::forward<Args>(args))...)
(20.9.2) shall be a valid expression.
And INVOKE
is defined thus:
[C++14: 20.9.2/1]:
Define INVOKE(f, t1, t2, ..., tN)
as follows:
(t1.*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous item;t1.*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;(*t1).*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is not one of the types described in the previous item;f(t1, t2, ..., tN)
in all other cases.
As you can see, there is no provision here for std::reference_wrapper<Worker>
, which is what std::ref(*this)
gives you. Certainly nothing in the decay rules helps ([C++14: 30.2.6/1]
).
Clang is actually jumping the gun a little here, seemingly allowing this because it is going to be standard-compliant one day thanks to our very own Jonathan Wakely filing Library Working Group issue #2219. But, for now, it's not.
Anyway, this whole thing is moot. There is no need to write this code. Just write this:
Worker() : m_worker(&Worker::doWork, this, 1)
Upvotes: 4