为什么不破坏被贴片新覆盖的对象的未定义行为?

kna*_*ten 9 c++ placement-new object-lifetime undefined-behavior language-lawyer

我试图弄清楚以下是否是未定义的行为.我觉得这不是UB,但我对标准的阅读使它看起来像是UB:

#include <iostream>

struct A {
    A() { std::cout << "1"; }
    ~A() { std::cout << "2"; }
};

int main() {
    A a;
    new (&a) A;
}
Run Code Online (Sandbox Code Playgroud)

引用C++ 11标准:

basic.life4说"程序可以通过重用对象占用的存储来结束任何对象的生命周期"

所以之后new (&a) A,原始A对象已经结束了它的生命周期.

class.dtor11.3表示"当创建对象的块退出时,将为具有自动存储持续时间([basic.stc.auto])的构造对象隐式调用析构函数([stmt.dcl])"

因此,退出A时会隐式调用原始对象的析构函数main.

class.dtor15说"如果为生命周期结束的对象([basic.life])调用析构函数,则行为是不确定的."

所以这是未定义的行为,因为原始A不再存在(即使新的a现在存在于同一存储中).

问题是是否A调用了原始的析构函数,或者是否调用了当前命名a的对象的析构函数.

我知道basic.life7,它表示名称a是指放置后的新对象new.但是class.dtor11.3明确地说它是对象的析构函数,它退出被调用的作用域,而不是从作用域的名称引用对象的析构函数.

我误读了标准,还是这个实际上是未定义的行为?

编辑:有几个人告诉我不要这样做.为了澄清,我绝对不打算在生产代码中这样做!这是针对CppQuiz问题,这是关于极端情况而不是最佳实践.

Lig*_*ica 5

你误读了它.

"对于构造对象隐式调用析构函数"......意味着存在它们并且它们的存在已经完全构建.虽然可以说没有完全拼写出来,但是A它不符合这个标准,因为它不再是"构造的":它根本不存在!只有新的/替换对象会被自动销毁,然后,最后main,正如您所期望的那样.

否则,这种新的布局形式将非常危险并且在语言中具有可争议的价值.然而,值得指出的是,A以这种方式重新使用实际有点奇怪和不寻常,如果没有其他原因导致只是这样的问题.通常,您将new-placement放入一些平淡的缓冲区(如一个char[N]或一些对齐的存储),然后自己也调用析构函数.

实际上可以在basic.life上找到类似于你的例子的东西.它是UB,但只是因为有人构建了T一个B; 措辞清楚地表明这是代码的唯一问题.

但这是关键:

在本国际标准中归于对象的属性仅在其生命周期内适用于给定对象.[..] [ basic.life3 ]

  • 很好找.你提到的位置实际上包含了宽恕OP特定情况的措辞:"当隐式析构函数调用发生时,程序**必须确保原始类型的对象占用相同的存储位置**;否则程序的行为是未定义".我的重点.注意"原始类型",而不是"原始对象";-). (2认同)

Rak*_*111 5

我是否误读了标准,或者这实际上是未定义的行为?

这些都没有。标准不明确,但可以更清楚。目的是调用新对象的析构函数,正如 [basic.life]p9 中暗示的那样。

[class.dtor]p12 不是很准确。我向 Core 询问了这件事,Mike Miller(一位非常资深的成员)说

I wouldn't say that it's a contradiction [[class.dtor]p12 vs [basic.life]p9], but clarification is certainly needed. The destructor description was written slightly naively, without taking into consideration that the original object occupying a bit of automatic storage might have been replaced by a different object occupying that same bit of automatic storage, but the intent was that if a constructor was invoked on that bit of automatic storage to create an object therein - i.e., if control flowed through that declaration - then the destructor will be invoked for the object presumed to occupy that bit of automatic storage when the block is exited - even it it's not the "same" object that was created by the constructor invocation.

一旦发布,我将使用 CWG 问题更新此答案。因此,您的代码没有 UB。