在父级的析构函数中加入成员线程访问其父级 class 的其他成员是否会导致未定义的行为?

Does joining a member thread accessing other members of its parent class in the parent's destructor result in 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;
}

这不是未定义的行为。如果我们查看 [[=​​17=]]/8,我们有

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X’s virtual base classes.

这说明 class 的非静态成员在析构函数的主体是 运行 之后被销毁。这意味着所有成员在析构函数中都处于活动状态,并且操作 x 和调用 join 的行为就像它们在普通成员函数中一样。唯一的区别是在析构函数的主体是 运行 之后,成员本身将被销毁。

使用 N3337 因为这个问题被标记为 C++11。

在析构函数执行期间访问class成员似乎不是未定义:

§12.7 [class.cdtor]/1

For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.

虽然上面写着

§12.4 [class.dtor]/15

Once a destructor is invoked for an object, the object no longer exists

以下在讨论对象时明确链接到 12.7:

§3.8 [basic.life]/5

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated 38 or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see 12.7.