将const引用传递给临时/匿名lambda到std :: thread构造函数是否安全?

时间:2018-04-23 14:00:23

标签: c++ lambda pass-by-reference stdthread

继续这个问题:can-a-temperary-lambda-by-passed-by-reference?

我有一个固定的代码段:

// 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) });
}

这似乎有效,但我担心将临时lambda传递给线程构造函数,因为线程将运行,但函数start_work()将返回,temp-lambda将超出范围。

但是我正在查看定义的std :: thread构造函数:

  

thread()noexcept; (1)(自C ++ 11起)

     

线程(线程&amp;&amp; other)noexcept; (2)(自C ++ 11起)

     

模板&LT; class Function,class ... Args&gt;   显式线程(功能&amp;&amp; f,Args&amp;&amp; ... args); (3)(自C ++ 11起)

     

thread(const thread&amp;)= delete; (4)(自C ++ 11起)

所以我假设构造函数3被调用:

template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );

我很难理解这里写的是什么,但看起来它会尝试移动lambda &&,我认为这对于临时变量是好的。

我在我的代码片段中做了什么危险(即参考范围超出范围)或更正(即临时移动,一切都很好)?或两者都不是?

另一种方法是传递我的价值(制作副本),在这种情况下无论如何都不是那么糟糕。

2 个答案:

答案 0 :(得分:8)

一个临时确实已被移动,但它是&#34;内部&#34;一,std::thread的论点。

暂时持有&#34;外部&#34;临时,start_work的参数,其生命周期在start_work返回后结束。

因此,你的内心&#34; lambda对象持有对在执行期间可能存在或可能不存在的对象的引用,这是非常不安全的。

答案 1 :(得分:3)

lambda是C ++中的匿名struct。如果我们要将片段翻译成没有lambdas的等效片段,那么它将成为

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有一个非基于堆栈的const引用作为成员,只要start_work返回,它就会悬挂,无论lambda对象本身是否被复制。