在下面的代码中:
#include <iostream>
#include <thread>
using namespace std;
class tester {
public:
tester() {
cout << "constructor\t" << this << "\n";
}
tester(const tester& other) {
cout << "copy cons.\t" << this << "\n";
}
~tester() {
cout << "destructor\t" << this << "\n";
}
void print() const {
cout << "print\t\t" << this << "\n";
}
};
int main() {
tester t;
cout << " before lambda\n";
thread t2([=] {
cout << " thread start\n";
t.print();
cout << " thread end\n";
});
t2.join();
cout << " after join" << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当使用cl.exe(在 Windows 上)编译时,我得到以下信息:
constructor 012FFA93
before lambda
copy cons. 012FFA92
copy cons. 014F6318
destructor 012FFA92
thread start
print 014F6318
thread end
destructor 014F6318
after join
destructor 012FFA93
Run Code Online (Sandbox Code Playgroud)
通过g++(在 WSL 上)我得到:
constructor 0x7ffff5b2155e
before lambda
copy cons. 0x7ffff5b2155f
copy cons. 0x7ffff5b21517
copy cons. 0x7fffedc630c8
destructor 0x7ffff5b21517
destructor 0x7ffff5b2155f
thread start
print 0x7fffedc630c8
thread end
destructor 0x7fffedc630c8
after join
destructor 0x7ffff5b2155e
Run Code Online (Sandbox Code Playgroud)
我预计[=]捕获将创建 1 个tester. 为什么有几个副本立即被销毁?
为什么 MSVC 和 GCC 之间存在分歧?这是未定义的行为还是什么?
该标准要求传递给构造函数的可调用对象std::thread是有效可复制构造的([thread.thread.constr])
\n\n授权:以下内容全部正确:
\n\n
\n- \n
is_\xc2\xadconstructible_\xc2\xadv<decay_\xc2\xadt<F>, F>- [...]
\n
is_\xc2\xadconstructible_\xc2\xadv<decay_\xc2\xadt<F>, F>是一样的is_copy_constructible(或者更确切地说,是相反的)。
这是为了允许实现自由地传递可调用对象,直到它到达被调用的位置。(事实上,标准本身建议可调用对象至少被复制一次。)
\n由于 lambda 被编译成一个小类,并且重载了函数调用运算符(函子),因此每次复制 lambda 时,它都会创建捕获实例的副本tester。
如果您不希望发生复制,您可以在捕获列表中引用您的实例:
\nthread t2([&ref = t] {\n cout << " thread start\\n";\n ref.print();\n cout << " thread end\\n";\n});\nRun Code Online (Sandbox Code Playgroud)\nconstructor 0x7ffdfdf9d1e8\n before lambda\n thread start\nprint 0x7ffdfdf9d1e8\n thread end\n after join\ndestructor 0x7ffdfdf9d1e8\nRun Code Online (Sandbox Code Playgroud)\n