placement new 的 return 值与其操作数的转换值之间是否存在(语义)差异?

Is there a (semantic) difference between the return value of placement new and the casted value of its operand?

placement new 的 return 值与其操作数的转换值之间是否存在(语义)差异?

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

Foo *a = new(buffer) Foo;
Foo *b = reinterpret_cast<Foo *>(buffer);

ab 有什么不同吗?


编辑:根据 DaBler 的评论,这个问题表明存在差异,如果使用 const/reference 成员:

所以,我的一点点更新问题:如果 Foo 没有 const 或引用成员,ab 有什么不同吗?

通过 a 访问是合法的,而 b 则不是。来自 [basic.compound]

Two objects a and b are pointer-interconvertible if:

  • they are the same object, or

  • one is a standard-layout union object and the other is a non-static data member of that object, or

  • one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object ([class.mem]), or

  • there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_­cast. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address.  — end note ]

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

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

An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_­cast<cv T*>(static_­cast<cv void*>(v)). [ Note: Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value.  — end note ]

只有 a 可以安全地用于直接访问放置 new-expression 创建的 Foo 对象(我们称之为 x 为了便于参考)。使用 b 需要 std::launder.

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

If the entity is a non-array object, the result of the new-expression is a pointer to the object created.

因此 a 的值为 "pointer to x"。当然,这个指针可以安全地用于访问 x.

reinterpret_cast<Foo*>(buffer) 将数组到指针的转换应用于 buffer(参见 [expr.reinterpret.cast]/1)。应用转换后的结果值为 "pointer to the first element of buffer"。 这是一个指向不同类型对象指针的对象指针的 reinterpret_cast,并且被 [expr.reinterpret.cast]/7.

定义为等同于 static_cast<Foo*>(static_cast<void*>(buffer))

void*的内部转换实际上是一个隐式转换。每 [conv.ptr]/2,

The pointer value is unchanged by this conversion.

因此,内部转换产生一个 void*,值为 "pointer to the first element of buffer"。

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

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.

  • If the original pointer value represents the address A of a byte in memory and A does not satisfy the alignment requirement of T, then the resulting pointer value is unspecified.

  • Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b.

  • Otherwise, the pointer value is unchanged by the conversion.

假设 buffer 已适当对齐(如果不是,您在此之前就会遇到麻烦),第一个项目符号不适用。第二个项目符号同样不适用,因为这里没有 pointer-interconvertiblity。因此,我们击中了第三颗子弹 - "the pointer value is unchanged by the conversion" 并保持 "pointer to the first element of buffer".

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

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

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