简体   繁体   中英

Use member function for `std::thread` with `std::ref(*this)` fails to compile

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.

Your thread constructor call relies on the following semantics:

[C++14: 30.3.1.2/3]: Requires: F and each Ti in Args 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) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T ;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
  • t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T ;
  • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM