What is the reason that you can't move an object to another std::thread
? There are situations where it could be useful. For example:
You create a loop that accepts incoming socket connections. It would be nice to move incoming connections to another thread that will handle the connections. You don't need the connection anymore in the accept loop. So why should you create a pointer?
A small test case:
#include <iostream>
#include <thread>
using namespace std;
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest &pointertest) {cout << "Copy";}
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
void foo(Pointertest &&pointertest)
{
}
int main()
{
Pointertest pointertest;
foo(std::move(pointertest)); //Works
thread test(foo,std::move(pointertest)); //**cannot convert parameter 1 from 'Pointertest' to 'Pointertest &&'**
}
The std::thread
constructor has to treat the arguments you give it somewhat differently from most forwarding functions.
The reason for this is due to questions of when the thread actually gets started. If the part of the function invocation that actually created the function argument gets run long after the thread
object is created (which is entirely legal behavior), then the object that needs to be moved from may have long since been destroyed.
Just consider an altered version of your code:
std::thread some_func()
{
Pointertest pointertest;
thread test(foo,std::move(pointertest));
return test;
}
This is perfectly valid (the thread will be moved out of the function). However, there's a big problem. foo
may not have been called yet. And since foo
takes its parameter by reference, it now has a reference to a stack variable that has been destroyed.
That's bad. But even if foo
took its parameter by value, it would change nothing. Because the actual movement into that parameter doesn't happen until some indeterminate time after the thread has been started. The attempt to move into the parameter will still use an rvalue reference to a stack variable that has been destroyed. Which again is bad.
Therefore, std::thread
constructor does something different. It copy/moves the arguments you give it into internal storage (this is done on the current thread). Then it uses those values as arguments for the actual function call (this is done on the new thread).
According to the standard, the thread constructor should treat pass these internal variables to your functions as temporaries. The standard specifically states INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)
, where the DECAY_COPY
stuff happens on the original thread, while the INVOKE
part happens on the new thread.
So it seems like your thread
implementation isn't able to forward the non-copyable parameters through correctly. You should be able to pass a non-copyable type; the arguments are only required to be MoveConstructible
.
So this would appear to be a bug in your implementation.
It is possible. Fixing the signature of your copy constructor makes it work for me:
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest const& pointertest) {cout << "Copy";}
// ^^^^^^
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
Also, do not forget to join your thread (or detach from it) before your thread
object goes out of scope:
int main()
{
Pointertest pointertest;
thread test(foo, std::move(pointertest));
test.join();
// ^^^^^^^^^^^^
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With