我的一位同事声称,一旦对象的析构函数调用开始,由线程(该对象本身的成员)完成对对象成员的所有访问都是UB。
这意味着如果线程正在访问任何对象的其他成员,则在对象的析构函数期间调用std::thread::join
是UB。
我在“Object Lifetime”下简要介绍了最新的标准草案,但找不到能给我一个确定答案的东西。
以下代码(on wandbox)是否会引入未定义的行为?该标准的一部分是什么澄清了这种互动?
struct A
{
atomic<bool> x{true};
thread t;
// Capturing 'this' is part of the issue.
// The idea is that accessing 'this->x' becomes invalid as soon as '~A()' is entered.
// vvvv
A() : t([this]
{
while(x)
{
this_thread::sleep_for(chrono::milliseconds(100));
}
})
{
}
~A()
{
x = false;
t.join();
}
};
int main()
{
A a;
}
答案 0 :(得分:6)
这不是未定义的行为。如果我们看[class.dtor] / 8,我们有
在执行析构函数的主体并销毁在主体内分配的任何自动对象之后,类X的析构函数调用X的直接非变量非静态数据成员的析构函数,X的直接基类的析构函数,如果X是派生类最多的类(12.6.2),它的析构函数调用X的虚拟基类的析构函数。
表示在运行析构函数体之后销毁类的非静态成员。这意味着所有成员在析构函数中都处于活动状态并操纵x
并且调用join
的行为就像在普通成员函数中一样。唯一的区别是在析构函数的主体运行之后,成员本身将被销毁。
答案 1 :(得分:3)
使用N3337,因为这个问题被标记为C ++ 11。
在析构函数执行期间访问类成员似乎没有定义:
§12.7[class.cdtor] / 1
对于具有非平凡析构函数的对象,请参阅any 析构函数后的对象的非静态成员或基类 完成执行导致未定义的行为。
虽然说明了
§12.4[class.dtor] / 15
为对象调用析构函数后,该对象不再存在
以下在讨论对象时明确链接到12.7:
§3.8[basic.life] / 5
在对象的生命周期开始之前但在存储之后 该对象将占用的是 38 或者, 在对象的生命周期结束之后和存储之前 被占用的对象被重用或释放,任何引用的指针 对象将位于或位于的存储位置可能是 使用但仅限于有限的方式。对于正在建造的物体或 破坏,见12.7。