Following on from this question: can-a-temperary-lambda-by-passed-by-reference?
I have a fixed code snippet:
// global variable
std::thread worker_thread;
// Template function
template <typename Functor>
void start_work(const Functor &worker_fn) // lambda passed by const ref
{
worker_thread = std::thread([&](){
worker_fn();
});
}
This is called like this:
void do_work(int value)
{
printf("Hello from worker\r\n");
}
int main()
{
// This lambda is a temporary variable...
start_work([](int value){ do_work(value) });
}
This seems to work, but I am concerned about passing a temporary lambda into the thread constructor since the thread will run, but the function start_work() will return and the temp-lambda will go out of scope.
However I was looking at the std::thread constructor which is defined:
thread() noexcept; (1) (since C++11)
thread( thread&& other ) noexcept; (2) (since C++11)
template< class Function, class... Args > explicit thread( Function&& f, Args&&... args ); (3) (since C++11)
thread(const thread&) = delete; (4) (since C++11)
So I am assuming that the constructor 3 is called:
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
I struggle to understand what is written here, but it looks like it would try to move the lambda &&
which I believe is ok for temp variables.
So is what I have done in my code snippet dangerous (i.e. the ref goes out of scope) or correct (i.e. the temp is moved and all is well)? or neither??
An alternative is to just pass my value (make a copy) which is not so bad anyway in this case.
A temporary is indeed moved but it is the "inner" one, the argument to std::thread
.
That temporary holds a reference to the "outer" temporary, the argument to start_work
, and whose lifetime ends after start_work
has returned.
Thus, your "inner" lambda object holds a reference to an object that may or may not exist during its execution, which is very unsafe.
A lambda is an anonymous struct
in C++. If we were to translate the snippet to an equivalent one without lambdas, it would become
template <typename Functor>
void start_work(const Functor &worker_fn)
{
struct lambda {
const Functor& worker_fn;
auto operator()() const { worker_fn(); }
};
worker_thread = std::thread(lambda{worker_fn});
}
lambda
has a non-stack-based const reference as a member, which would dangle as soon as start_work
returns, regardless whether the lambda
object itself is copied.
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