Pow*_*ice 33 c++ undefined-behavior language-lawyer
https://en.cppreference.com/w/cpp/language/lifetime在注释部分有这个代码,这里转载:
struct A {
int* p;
~A() { std::cout << *p; } // if n outlives a, prints 123
};
void f() {
A a;
int n = 123; // if n does not outlive a, this is optimized out (dead store)
a.p = &n;
}
Run Code Online (Sandbox Code Playgroud)
在本笔记部分试图说些什么?
根据我的理解,代码是UB(或者是它),因为它显然n不会超过a.
它是什么意思:
非类对象(存储持续时间结束)和类对象(构造的逆序)之间的生命周期规则的差异很重要
但它没有说明如何.
整个部分让我很困惑.
Nic*_*las 27
这是C++生命周期规则的一个奇怪方面.[basic.life]/1告诉我们一个对象的生命周期结束:
- 如果
T是具有非平凡析构函数([class.dtor])的类类型,则析构函数调用将启动,或者- 对象占用的存储被释放,或被未嵌套在o中的对象重用([intro.object]).
强调补充说.int不是"具有非平凡析构函数的类类型",因此它的生命周期仅在它占用的存储空间被释放时结束.相比之下,A是一个带有非平凡析构函数的类类型,因此它的生命周期在析构函数被调用时结束.
根据[basic.stc.auto]/1,当作用域退出时,将释放作用域的存储:
[具有自动存储持续时间的变量]的存储将持续到创建它们的块退出.
但是根据[stmt.jump]/2销毁自动变量:
退出范围(无论多么已完成)时,在该范围内构建的具有自动存储持续时间的对象将按其构造的相反顺序销毁.
请注意,指定了销毁顺序,但未指定自动存储释放的顺序.这意味着实现可以在每个变量被销毁之后立即释放存储,或者稍后将其全部释放,或以某种任意其他顺序释放.
现在,它使用单数进行存储("存储为...持续")而不是单独讨论每个变量这一事实可能表明目的是为整个存储立即释放该范围.但是标准中没有明确说明这一点.因此,只要变量在其存储被释放之前被销毁,任何破坏与释放的排序似乎都是合法的.
这意味着代码完全可以工作,并且n能够活得更久a.但它是否确实有效尚未明确.
xsk*_*xzr 10
此示例借用了Core Language Issue 2256:
条: 6.8 [basic.life] 状态:起草 提交者: Richard Smith 日期: 2016-03-30
根据6.4 [basic.lookup] bullet 1.4,下面的例子定义了行为,因为生命周期
n延长直到它的存储被释放,这是在a析构函数运行之后:Run Code Online (Sandbox Code Playgroud)void f() { struct A { int *p; ~A() { *p = 0; } } a; int n; a.p = &n; }如果所有对象的生命周期结束,无论它们是否具有非平凡的析构函数,都会被视为相同,这将更加一致.
2018年3月会议记录:
CWG同意建议的方向.
关键的想法是,对象的生命周期是在其破坏时结束还是在其释放内存时可能会影响程序的语义.在这个例子中,
如果n破坏结束的生命周期n,程序是不确定的;
如果在n内存释放之前结束的生命周期,程序已定义行为1.
因此,需要进一步讨论以确定对象的寿命何时结束.
1这是因为核心语言问题2115:
条: 9.6 [stmt.jump] 状态:起草 提交者: Richard Smith 日期: 2015-04-16
标准未指定从块退出时销毁自动变量与释放变量存储之间的相对顺序:是先执行所有析构函数,然后释放存储,还是将它们交错?
2016年2月会议记录:
CWG同意存储应该持续到所有破坏都完成,尽管"as-if"规则允许对这种排序进行不可观察的优化.
目的是在所有破坏完成后发生自动变量的内存释放.