放置new的返回值与其操作数的转换值之间是否存在(语义)差异?

gez*_*eza 13 c++ strict-aliasing placement-new language-lawyer reinterpret-cast

放置new的返回值与其操作数的转换值之间是否存在(语义)差异?

struct Foo { ... };
char buffer[...];

Foo *a = new(buffer) Foo;
Foo *b = reinterpret_cast<Foo *>(buffer);
Run Code Online (Sandbox Code Playgroud)

是否ab以某种方式有什么不同?


编辑:根据DaBler的评论,这个问题告诉我,如果使用const/reference成员则存在差异:使用const成员放置新类和赋值

所以,我的小位的更新问题:是否ab以任何方式不同,如果Foo没有const或引用成员?

T.C*_*.C. 15

只能a安全地用于直接访问Foo由放置new-expression创建的对象(我们将调用它x以便于参考).使用b要求std::launder.

a[expr.new]/1中指定:

如果实体是非数组对象,则new-expression的结果 是指向创建的对象的指针.

因此,值a是"指向x".当然,这个指针可以安全地用于访问x.

reinterpret_cast<Foo*>(buffer)应用数组到指针的转换buffer(参见[expr.reinterpret.cast]/1).应用转换后的结果值是"指向第一个元素的指针buffer".这是一个reinterpret_cast对象的指针到一个不同类型的对象的指针,并且被定义为等同于static_cast<Foo*>(static_cast<void*>(buffer))[expr.reinterpret.cast]/7.

内部void*强制转换实际上是一种隐式转换.每[conv.ptr]/2,

此转换不会改变指针值.

因此,内部强制转换产生一个void*值为"指向第一个元素的指针buffer".

外部演员由[expr.static.cast]/13管理,我已经轻轻地重新格式化为要点:

"到指针类型的prvalue CV1 void "可以被转换成类型的prvalue"指针CV2 T ",其中T是一个对象类型和CV2是相同的CV-资格,或更大的CV-资格比,CV1.

  • 如果原始指针值表示A内存中字节的地址并且A不满足对齐要求T,则未指定结果指针值.

  • 否则,如果原始指针值指向一个对象a,并且存在一个b类型T(忽略cv-qualification)的对象,该对象是指针可互换的a,则结果是指向的对象 b.

  • 否则,转换指针值不变.

假设它buffer是适当对齐的(如果不是,那么在此之前你会遇到麻烦),第一个子弹是不适用的.第二个子弹同样不适用,因为这里没有指针 - 互换性.接下来我们点击第三个子弹 - "转换时指针值不变"并保持"指向第一个元素的指针buffer".

因此,b不指向Foo对象x; 它指向第一个char元素buffer,即使它的类型是Foo*.因此它不能用于访问x; 尝试这样做会产生未定义的行为(对于非静态数据成员的情况,通过省略[expr.ref] ;对于非静态成员函数的情况,通过[class.mfct.non-static]/2).

要恢复指向xfrom 的指针b,std::launder可以使用:

b = std::launder(b); // value of b is now "pointer to x"
                     // and can be used to access x
Run Code Online (Sandbox Code Playgroud)


Pas*_* By 5

通过访问a是合法的,而通过b是不合法的。来自[基本化合物]

\n
\n

两个对象ab是指针可相互转换的,如果:

\n
    \n
  • 它们是同一个对象,或者

    \n
  • \n
  • 一个是标准布局联合对象,另一个是该对象的非静态数据成员,或者

    \n
  • \n
  • 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的第一个基类子对象 ([class.mem] ), 或者

    \n
  • \n
  • 存在一个对象c,使得ac是指针可相互转换的,并且cb是指针可相互转换的。

    \n
  • \n
\n

如果两个对象是指针可相互转换的,那么它们具有相同的地址,并且可以通过reinterpret_\xc2\xadcast. [\xe2\x80\x89注意:数组对象及其第一个元素不可进行指针互换,即使它们具有相同的地址。\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89尾注\xe2\x80\x89]

\n
\n

它们不是同一个对象,不是联合体,也不是彼此的子对象,因此不能进行指针相互转换。

\n

注意[expr.reinterpret.cast]仅保证reinterpret_cast<char*>(b) == buffer.

\n
\n

对象指针可以显式转换为不同类型的对象指针。当对象指针类型的纯右值v 转换为对象指针类型 \xe2\x80\x9cpointer to cv T\xe2\x80\x9d 时,结果为static_\xc2\xadcast<cv T*>(static_\xc2\xadcast<cv void*>(v))。[\xe2\x80\x89注意:将 \xe2\x80\x9cpointer 类型的纯右值转换为T1\xe2\x80\x9d 到 \xe2\x80\x9cpointer 类型的纯右值到T2\xe2\x80\x9d (其中T1T2是对象类型,其中的对齐要求T2并不比T1) 更严格,并且返回到其原始类型会产生原始指针值。\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89尾注\xe2\x80\x89]

\n
\n