1

I want to start a std::thread at a member function of an object that does not have a copy constructor. The standard way of starting a thread at a member function (see for example Start thread with member function) needs a copy constructor. I thought I could get around this by using std::ref (see for example std::thread pass by reference calls copy constructor), but no luck.

Here is my code. Thread t1 works, but not t2. If I try to uncomment the two lines, it gives:

error: attempt to use a deleted function 
__invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...); ^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/thread:357:5: note: 
in instantiation of function template specialization
'std::__1::__thread_execute<void (Dummy::*)(), std::__1::reference_wrapper<Dummy> , 1>' 
requested here __thread_execute(*__p, _Index()); ^ 
etc.

How do I start the thread directly at print(), without needing uselessFunction?

Also, I'd like to understand the error better. Which "deleted function" is the compiler complaining about?

#include <iostream>
#include <thread>
using std::cout;
using std::endl;

class Dummy {
public:
    std::atomic<bool> flag;  // atomic kills implicitly created copy constructor
    void print() { std::cout << "hello!" << std::endl; }
};

void uselessFunction(Dummy &d) {
    d.print();
}

int main() {
    Dummy d{};
    std::thread t1(uselessFunction, std::ref(d));  // this works
    // std::thread t2(&Dummy::print, std::ref(d)); // does not work
    t1.join();
    // t2.join();
}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Martin Cook
  • 554
  • 5
  • 15
  • What if you pass a pointer? `Also, I'd like to understand the error better... which "deleted function" is the compiler complaining about?` Probably the copy constructor, the error message should tell you which function. – tkausl Aug 24 '18 at 12:54
  • What compiler are you using? I cannot get an error for `t2`: https://wandbox.org/permlink/kCZrnpBMKPAK0enX – NathanOliver Aug 24 '18 at 12:54
  • I am using clang. The error looks like this, which I don't quite understand: `error: attempt to use a deleted function __invoke(_VSTD::move(_VSTD::get<0>(__t)), _VSTD::move(_VSTD::get<_Indices>(__t))...); ^ /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/thread:357:5: note: in instantiation of function template specialization 'std::__1::__thread_execute , 1>' requested here __thread_execute(*__p, _Index()); ^` etc. – Martin Cook Aug 24 '18 at 12:56
  • @MartinCook Which version of clang? – NathanOliver Aug 24 '18 at 12:56
  • @NathanOliver `Apple LLVM version 8.0.0 (clang-800.0.42.1)` – Martin Cook Aug 24 '18 at 12:57
  • @NathanOliver and it should be c++11 – Martin Cook Aug 24 '18 at 12:59
  • 1
    @MartinCook OK. I was able to [reproduce this error with clang 3.8.1 or lower](https://wandbox.org/permlink/YMINeS8P5stKCFUj). I went back to gcc's 4.8.2 and it compiled without issue. I'm suspecting this may have been a clang bug that has since been fixed. The workaround in my answer should work for you. – NathanOliver Aug 24 '18 at 13:07
  • @NathanOliver Yes, I can confirm that this is compiler version issue. It compiled fine when I used g++ `g++-8 (Homebrew GCC 8.1.0) 8.1.0`. Btw, thanks for showing me wandbox, that is a great site. – Martin Cook Aug 24 '18 at 13:10
  • @MartinCook It is great for trying to find out if something is a compiler bug. Or just what version you would need to update to to support some code you want to run. – NathanOliver Aug 24 '18 at 13:11

1 Answers1

2

std::thread use std::invoke to call the function. std::invoke is smart enough to be able to take a pointer to an object and call a member function with it. That means you can change t2 to

std::thread t2(&Dummy::print, &d);

and get the correct behavior.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402