Безопасно ли передавать константную ссылку на временную / анонимную лямбду в конструктор 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 Сам объект копируется.

Другие вопросы по тегам