(const) 类对象销毁期间的成员访问

use*_*522 5 c++ multithreading lifetime language-lawyer

如果添加任一注释行,以下程序是否具有 UB?

#include<atomic>
#include<iostream>
#include<thread>

std::atomic<bool> a = false;

struct T {
    int i;
    void reset() { i = 0; }
    ~T() {
        while(!a);
        //reset();
        a = false;
        while(!a);
    }
};

int main() {
    std::thread x;
    {
        const T t{42};
        x = std::thread([&t]{
            std::cout << t.i << "\n";
            a = true;
            while(a);
            //const_cast<T&>(t).reset();
            //const_cast<int&>(t.i) = 0;
            std::cout << t.i << "\n";
            a = true;
        });
    }
    x.join();
}
Run Code Online (Sandbox Code Playgroud)

GCC 打印42后跟0,而 Clang 打印42两次:https://godbolt.org/z/j544rq3cx

如果t我不直接在线程中使用,而是捕获对t.i(在析构函数调用开始之前同步)的引用并通过该引用进行读取和写入,该怎么办?在这种情况下,成员访问不必t在生命周期之外发生。有趣的是,我无法使用42这样的构造让 Clang 打印两次。


直觉上我已经说过 UB 了,因为i它是在生命周期之外访问的t(在调用析构函数时结束),但是此时t处于“被破坏”状态并且适用不同的规则,并且我没有找到任何明确说明 UB 的内容。

另外,为了避免混淆,我认为conston不t应该对其是否为 UB 产生任何影响。然而,这const表明这对编译器优化有影响,正如 Clang 处理它的方式所示。除了在构造和销毁期间之外const,不能修改自动存储持续时间对象,也不能替换该对象,因此编译器通常可以假设i不会更改其值,即使对t转义到未知函数的引用也是如此。