Suppose I have the following structure :
struct A {
A() { std::cout << "default constructor" << std::endl; }
A(const A&) { std::cout << "copy constructor" << std::endl; }
A(A&&) { std::cout << "move constructor" << std::endl; }
~A() {std::cout << "deleting" << std::endl; }
void update() { std::cout << "updating" << std::endl;}
};
I have a generic function that will interact with a reference of A :
void sync_process(A&) {
/// ...
}
I want to process an instance in a different thread. Here is the code I use (basically) :
struct B {
std::list<std::thread> threads;
~B() {
for(std::thread& th : threads)
th.join();
}
void thread_process(A& a) {
// call sync_process in another thread for a copy of A
}
};
And the main code would be something like this :
B b;
{
A a;
b.thread_process(a);
}
Note that a
may be deleted just after calling thread_process
.
Attempt 1
I first tried something like this :
threads.emplace_back([=]{sync_process(a);});
I expected the capture to let me use the reference, considering I got my own copy of a
. But I was surprised to get a const A
instead, resulting the following error :
// invalid initialization of reference of type 'A&' from expression of type 'const A'
Attempt 2
A cpy(a);
threads.emplace_back(&sync_process, std::move(cpy));
This time : this weird error appears no type named 'type' in 'class std::result_of<void (*(A))(A&)>'
. However, using a constant reference on sync_process
prevent this error.
Attempt 3
threads.emplace_back([&]{A cpy(a); sync_process(cpy);});
This time, I'm making the copy in the thread. But I won't have any guarantee that my object is still alive at this moment, right ?
Attempt 4
A* cpy = new A(a);
threads.emplace_back([=]{sync_process(*cpy); delete cpy;});
OK, it works but it's not very safe : exceptions ...
So here are my questions :
- Why the lambda capture "const-qualify" my object ?
- What is the more elegant way to achieve this ? (store data with the thread, duplicate copies, ...)