(const)类对象销毁期间的成员访问是否存在未定义行为?
C++程序未定义行为分析
核心问题
取消注释下述代码中的任意一行注释后,程序是否存在未定义行为(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(); }
编译器行为差异
GCC运行后输出42后接0,而Clang两次输出42。
场景分析
原线程捕获t引用的情况
直觉判断这属于未定义行为:t的生存期在析构函数调用时结束,线程中对t.i的访问跨越了对象生存期。不过t此时处于“销毁中”的特殊阶段,目前尚未找到C++标准中明确判定此情况为UB的条款。
修改为捕获t.i引用的场景
如果不在线程中直接使用t,而是在析构函数调用开始前同步捕获t.i的引用,后续通过该引用进行读写操作——这种情况下无需在对象生存期外访问t的成员。有意思的是,这种写法下Clang不会再出现两次输出42的情况。
const限定符的影响
t的const限定符本身不决定是否构成UB,但会影响编译器的优化逻辑,比如Clang的处理方式。除了对象构造和销毁期间,自动存储期的const对象不可被修改或替换,因此编译器通常会假设i的值不会发生变化,哪怕t的引用逃逸到了未知函数中。
内容的提问来源于stack exchange,提问作者user17732522
相关产品推荐
相关产品推荐

