【问题标题】:Is it safe to pass const reference to temporary/annonymous lambda into std::thread constructor?将临时/匿名 lambda 的 const 引用传递给 std::thread 构造函数是否安全?
【发布时间】:2018-04-23 14:00:22
【问题描述】:

接着这个问题:can-a-temperary-lambda-by-passed-by-reference?

我有固定码sn-p:

// 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 起)

thread(thread&& other) noexcept; (2)(C++11 起)

模板 显式线程( Function&& f, Args&&... args ); (3)(C++11 起)

线程(常量线程&)=删除; (4)(C++11 起)

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

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

我很难理解这里写的内容,但它看起来会尝试移动 lambda &amp;&amp;,我认为这对于临时变量是可以的。

那么我在我的代码 sn-p 中所做的事情是危险的(即 ref 超出范围)还是正确的(即温度已移动并且一切正常)?还是都没有??

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

【问题讨论】:

  • 按承诺升级。完整的答案高于我的工资等级。
  • 你有一个悬空引用。
  • 问题是问是否可以将const &amp; 传递给std::thread 的临时对象,并且可以。但这不是你在做什么。相反,这里的问题是您在 lambda 中通过引用捕获了一个临时对象,这不是一回事,在这种情况下也不行。
  • 简而言之,lambda 可能会被正确移动,但它捕获的东西不会。作为一个骗子,我认为 lambdas 只不过是花哨的函数对象。
  • @code_fodder 考虑将 worker_fn 设为转发引用 (Functor&amp;&amp; worker_fn) 并使用 std::forward 初始化捕获。

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


【解决方案1】:

A 临时确实被移动了,但它是“内部”的,即std::thread 的参数。

该临时文件包含对“外部”临时文件的引用,即start_work 的参数,其生命周期在start_work 返回后结束。

因此,您的“内部” lambda 对象包含对在其执行期间可能存在或可能不存在的对象的引用,这是非常不安全的。

【讨论】:

  • 啊,好吧...所以在“理论”中,我可以将内部捕获更改为[=] 以通过副本捕获,这应该是安全的(会有一份,但最好是两份) ?
  • @code_fodder 只要你确保你正在捕获的东西也没有捕获任何可以悬空的引用,是的。
【解决方案2】:

lambda 是 C++ 中的匿名 struct。如果我们将 sn-p 翻译成没有 lambda 的等价的,它会变成

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 对象本身是否被复制。

【讨论】:

  • 是不是因为我通过引用捕获(例如[&amp;])而我可以通过复制捕获[=]
  • @code_fodder 是的。如果通过复制捕获,则为Functor worker_fn;
猜你喜欢
  • 2017-05-19
  • 2021-11-07
  • 1970-01-01
  • 2023-03-31
  • 2016-12-04
  • 1970-01-01
  • 2014-01-29
  • 1970-01-01
  • 2012-08-13
相关资源
最近更新 更多