这似乎是编译器错误,但同时发生在gcc和clang(可能还有其他)中。
如果一个函数接受一个lambda f
并从另一个调用g
的lambda f
创建一个线程,则f
可以悄悄地失去对其捕获变量的访问权限。这是一个示范。
#include <thread>
#include <iostream>
using std::cout;
using std::endl;
using std::thread;
template<typename F>
__attribute__((noinline)) // inlining hides the bug
thread* callMeLater1(F f)
{
return new thread(
[&]() {
// ...
// Insert sleep logic.
// ...
f();
});
}
template<typename F>
__attribute__((noinline))
thread* callMeLater2(F f)
{
return new thread(
[f]() {
// ...
// Insert sleep logic.
// ...
f();
});
}
int main(int argc, const char * argv[]) {
int a = 42;
cout << "orign: " << ((void*) &a) << endl;
auto f = [&]() {
cout << "later: " << ((void*) &a) << endl;
};
thread* t1 = callMeLater1(f);
t1->join();
delete t1;
thread* t2 = callMeLater2(f);
t2->join();
delete t2;
return 0;
}
这是结果。
orign: 0x7ffee88727ac
later: 0
later: 0x7ffee88727ac
在f
内部对callMeLater1
的调用已损坏。 f
对a
的访问将被破坏,从而导致无提示的错误或段错误。当在f
内部调用callMeLater2
时,不使用默认捕获&
时,该函数将正常工作。
这是预期的吗?
答案 0 :(得分:2)
这是未定义的行为。
lambda通过引用捕获callMeLater1
的参数。当然,callMeLater1
返回后,此参数将被销毁。
没有任何东西可以保证您新的执行线程在callmeLater1
返回之前调用传入的闭包。这样,传入的可调用对象可能在新线程尝试调用它之前被销毁,从而导致不确定的行为。