Безопасно ли передавать константную ссылку на временную / анонимную лямбду в конструктор std::thread?
Исходя из этого вопроса: может ли темпераментная лямбда проходить мимо по ссылке?
У меня есть фиксированный фрагмент кода:
// 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();
});
}
Это называется так:
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) });
}
Кажется, это работает, но я обеспокоен передачей временной лямбды в конструктор потока, поскольку поток будет работать, но функция start_work() вернется, а временная лямбда выйдет из области видимости.
Однако я смотрел на конструктор std::thread, который определен:
thread () noexcept; (1) (начиная с C++11)
нить (нить и другие) noexcept; (2) (начиная с C++11)
template
явный поток ( Function&& f, Args&&... args); (3) (начиная с C++11) нить (постоянная нить &) = удалить; (4) (начиная с C++11)
Поэтому я предполагаю, что конструктор 3 называется:
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
Я изо всех сил пытаюсь понять, что здесь написано, но похоже, что он попытается переместить лямбду &&
Я считаю, что это нормально для временных переменных.
Так что то, что я сделал в своем фрагменте кода, опасно (то есть ссылка выходит за рамки) или правильно (то есть темп перемещен, и все хорошо)? или нет??
Альтернатива - просто передать мое значение (сделать копию), что в любом случае не так уж и плохо.
2 ответа
Временный действительно перемещен, но это "внутренний" аргумент std::thread
,
Этот временный объект содержит ссылку на "внешний" временный аргумент start_work
и чье время жизни заканчивается после start_work
вернулся.
Таким образом, ваш "внутренний" лямбда-объект содержит ссылку на объект, который может существовать или не существовать во время его выполнения, что очень небезопасно.
Лямбда - аноним struct
в C++. Если бы мы перевели фрагмент к эквивалентному без лямбд, он стал бы
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
имеет не основанную на стеке константную ссылку в качестве члена, которая будет свисать, как только start_work
возвращается, независимо от того, lambda
Сам объект копируется.