加入成员线程访问父析构函数中其父类的其他成员会导致未定义的行为吗?

时间:2016-11-03 13:56:30

标签: c++ multithreading c++11 language-lawyer undefined-behavior

我的一位同事声称,一旦对象的析构函数调用开始,由线程(该对象本身的成员)完成对对象成员的所有访问都是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;
}

2 个答案:

答案 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。