reinterpret_cast创build一个普通的默认构造对象

cppreference 指出:

具有普通默认构造函数的对象可以通过在任何适当alignment的存储上使用reinterpret_cast来创build,例如,在使用std::malloc分配的std::malloc

这意味着以下是明确的代码:

 struct X { int x; }; alignas(X) char buffer[sizeof(X)]; // (A) reinterpret_cast<X*>(buffer)->x = 42; // (B) 

三个问题如下:

  1. 那个报价是正确的吗?
  2. 如果是的话,那么X的使用寿命到了什么时候? 如果在线(B) ,它是否被视为获取存储的本身? 如果在线(A) ,如果在(A)(B)之间有一个分支会有条件地构build一个X或其他吊舱, Y
  3. 在这方面,C ++ 11和C ++ 1z之间有什么变化吗?

请注意,这是一个旧的链接。 这个问题的措辞已经改变了。 它现在写道:

然而,与C不同的是,具有简单默认构造函数的对象不能通过简单地重新解释适当alignment的存储来创build,例如使用std::malloc分配的std::malloc :placement-new需要正式引入一个新对象,并避免潜在的未定义行为。

没有X对象,活着或其他,所以假装有一个导致未定义的行为。

当创build对象时, [intro.object] / 1详尽阐述:

一个对象是由一个定义([basic.def]),一个新的expression式 ([expr.new]),当隐式地改变一个联合的活动成员([class.union]),或者当一个临时对象([conv.rval],[class.temporary])。

通过P0137R1 ,这一段是“物体”一词的定义。

有没有X对象的定义? 没有。有新表情吗? 没有工会吗? 没有。您的代码中是否有一个语言构造创build临时X对象? 没有。

不pipe[basic.life]如何说空间初始化的对象的生命周期都是不相关的。 为了应用,你必须首先有一个对象。 你没有。

C ++ 11有着大致相同的段落,但并没有将它用作“对象”的定义。 尽pipe如此,解释是一样的。 另一种解释 – 把[basic.life]视为一旦获得合适的存储空间就创build一个对象 – 意味着你正在创造薛定谔的对象* ,这与N3337 [intro.object] / 6 :

如果一个对象是另一个对象的子对象,或者如果至less有一个是大小为零的基类子对象,并且它们是不同types的,则两个不是位域的对象可以具有相同的地址; 否则应有明确的地址。


*对于typesT ,具有适当的alignment和尺寸的存储是根据定义存储的,对于其尺寸和alignment要求等于或小于T 其他types的适当alignment和尺寸。 因此,该解释意味着同时获得存储器在所述存储器中创build具有不同types的无限的一组对象,全部具有相同的地址。

这个分析基于n4567,并使用它的部分编号。

§5.2.10/ 7:当对象指针types的prvalue v被转换为对象指针types“指向cv T的指针”时,结果是static_cast<cv T*>(static_cast<cv void*>(v))

因此,在这种情况下, reinterpret_cast<X*>(buffer)static_cast<X *>(static_cast<void *>(buffer)) 。 这让我们看看static_cast的相关部分:

§5.2.9/ 13:types“指向cv1 void的指针”的值可以转换为types“指向cv2 T的指针”的prvalue,其中T是一个对象types, cv2cv2条件相同或更大cv-qualification比cv1 。 空指针值被转换为目标types的空指针值。 如果原始指针值代表内存中一个字节的地址A ,且A满足T的alignment要求,则所得到的指针值代表与原始指针值相同的地址,即A

我相信这足以说明原始报价是正确的 – 这种转换给出了定义的结果。

至于一生,这取决于你在谈论什么一生。 转换会创build一个指针types的新对象 – 一个临时的对象,该对象具有从该对象所在的行开始的生命周期,并在其超出范围时结束。 如果有两个不同的转换条件发生,每个指针都有一个从创build它的转换位置开始的生命周期。

这些都不会影响提供底层存储的对象的生命周期,它仍然是buffer ,并且具有完全相同的生命周期,无论您是否创build指向该存储的指针(相同或转换types)。