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)
三个问题如下:
- 那个报价是正确的吗?
- 如果是的话,那么
X
的使用寿命到了什么时候? 如果在线(B)
,它是否被视为获取存储的本身? 如果在线(A)
,如果在(A)
和(B)
之间有一个分支会有条件地构build一个X
或其他吊舱,Y
? - 在这方面,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, cv2与cv2条件相同或更大cv-qualification比cv1 。 空指针值被转换为目标types的空指针值。 如果原始指针值代表内存中一个字节的地址A
,且A
满足T
的alignment要求,则所得到的指针值代表与原始指针值相同的地址,即A
我相信这足以说明原始报价是正确的 – 这种转换给出了定义的结果。
至于一生,这取决于你在谈论什么一生。 转换会创build一个指针types的新对象 – 一个临时的对象,该对象具有从该对象所在的行开始的生命周期,并在其超出范围时结束。 如果有两个不同的转换条件发生,每个指针都有一个从创build它的转换位置开始的生命周期。
这些都不会影响提供底层存储的对象的生命周期,它仍然是buffer
,并且具有完全相同的生命周期,无论您是否创build指向该存储的指针(相同或转换types)。