您使用通常放置 new 的版本确实很好。
§§ 3.8/1 和 3.8/4有一个解释1,其中平凡类型的对象能够按需“消失”和“出现”。这不是允许无视别名规则的免费通行证,因此请注意:
std::uint16_t storage[2];
static_assert( /* std::uint16_t is not a character type */ );
static_assert( /* storage is properly aligned for our purposes */ );
auto read = *reinterpret_cast<std::uint32_t*>(&storage);
// At this point either we’re attempting to read the value of an
// std::uint16_t object through an std::uint32_t glvalue, a clear
// strict aliasing violation;
// or we’re reading the indeterminate value of a new std::uint32_t
// object freshly constructed in the same storage without effort
// on our part
另一方面,如果您在第二个片段中交换了演员表(即先重新解释和编写),那么您也不完全安全。虽然在解释下,您可以证明写入发生在std::uint32_t
隐式重用存储的新对象上,但后续读取的形式为
auto value2 = *reinterpret_cast<int32_t*>(storage);
§3.8/5 说(强调我的并且非常相关):
[…] 在对象的生命周期结束之后,在对象占用的存储空间被重用或释放之前,任何指向对象将要或曾经位于的存储位置的指针都可以使用,但只能以有限的方式使用。[…] 这样的指针是指分配的存储空间(3.7.4.2),并且使用指针就像指针是 type 一样void*
,是明确定义的。
§3.8/6 是相同的,但采用引用/glvalue 形式(可以说更相关,因为我们在这里重用名称而不是指针,但该段落更难以脱离上下文理解)。另请参阅§3.8/7,它提供了一些我认为不适用于您的情况的有限余地。
为了简单起见,剩下的问题是:
T object;
object.~T();
new (&object) U_thats_really_different_from_T;
&object; // Is this allowed? What does it mean?
static_cast<void*>(&object); // Is this?
如果存储的类型碰巧涉及普通或无符号字符类型(例如,您storage
确实有 type unsigned char[4]
),那么我会说您有理由证明形成指向新对象存储的指针/引用是正确的(以后可能会重新解释)。再次参见例如¶¶ 5 和 6,它们具有用于形成指针/引用/glvalue 的显式转义子句和 §1.8描述对象如何涉及组成字节数组的C++ 对象模型。管理指针转换的规则应该简单明了且没有争议(至少相比之下……)。
1:很难衡量这种解释在社区中的接受程度——我在 Boost 邮件列表中看到了它,那里对它持怀疑态度