我可以通过 placement-new 覆盖 const 对象吗?
Can I overwrite a const object via placement-new?
Basic.life/8 告诉我们,我们可以使用对象占用的存储空间在其生命周期结束后创建一个新对象,并使用其原始名称来引用它,除非:
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and [...]
强调我的
但是,就在它的下方,我们可以看到一条说明:
- If these conditions are not met, a pointer to the new object can be obtained from a pointer that represents the address of its storage by calling
std::launder
这解释了 。我们可以拥有一个具有 const
成员的 class 类型,并使用 placement-new 在那里创建一个具有不同内部值的新对象。
让我吃惊的是第一句话的第一部分。它清楚地表明,如果存储是const
(不一定包含const
成员,但整个对象被声明为const
),我们不能使用它来引用新对象。 ,但这可能意味着 std::launder
可能会修复它。
但是我们如何创建这样的对象呢?最简单的例子失败了:
const auto x = 5;
new (&x) auto(10);
因为我们不能使用const void*
作为placement-new的缓冲区。我们可以 const_cast
它,但是抛弃“真实” const
ness 是未定义的行为。我相信这里也是如此。
如果只是禁止使用 const
对象作为放置新缓冲区,我会理解,但如果是这样,那么第一个引用中强调部分的目的是什么?我们可以将真正 const
对象的存储重用到另一个对象吗?
const 对象可以有指向其位置的非 const 指针。
struct bar{ int x=5; };
struct foo{
const bar b;
};
foo f;
::new (&f) foo{{3}};
这里我有一个 const int f.b.x
,我销毁它并构造一个值为 3 的新的。
char buff[sizeof(foo)];
foo const* pf=::new(buff)foo;
foo const* pf2=::new(buff)foo{{3}};
或许也可以通过联合来实现。
显然,只需要查看 低于我链接的标准部分的 2 个项目。 Basic.life/10 告诉我们:
Creating a new object within the storage that a const complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
它附带了一个示例:
struct B {
B();
~B();
};
const B b;
void h() {
b.~B();
new (const_cast<B*>(&b)) const B; // undefined behavior
}
这最终让我得出一个结论,在真正 const
对象。因此,我认为问题中提到的注释(参考点8)是错误的——它应该排除所讨论的情况。
Basic.life/8 告诉我们,我们可以使用对象占用的存储空间在其生命周期结束后创建一个新对象,并使用其原始名称来引用它,除非:
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and [...]
强调我的
但是,就在它的下方,我们可以看到一条说明:
- If these conditions are not met, a pointer to the new object can be obtained from a pointer that represents the address of its storage by calling
std::launder
这解释了 const
成员的 class 类型,并使用 placement-new 在那里创建一个具有不同内部值的新对象。
让我吃惊的是第一句话的第一部分。它清楚地表明,如果存储是const
(不一定包含const
成员,但整个对象被声明为const
),我们不能使用它来引用新对象。 ,但这可能意味着 std::launder
可能会修复它。
但是我们如何创建这样的对象呢?最简单的例子失败了:
const auto x = 5;
new (&x) auto(10);
因为我们不能使用const void*
作为placement-new的缓冲区。我们可以 const_cast
它,但是抛弃“真实” const
ness 是未定义的行为。我相信这里也是如此。
如果只是禁止使用 const
对象作为放置新缓冲区,我会理解,但如果是这样,那么第一个引用中强调部分的目的是什么?我们可以将真正 const
对象的存储重用到另一个对象吗?
const 对象可以有指向其位置的非 const 指针。
struct bar{ int x=5; };
struct foo{
const bar b;
};
foo f;
::new (&f) foo{{3}};
这里我有一个 const int f.b.x
,我销毁它并构造一个值为 3 的新的。
char buff[sizeof(foo)];
foo const* pf=::new(buff)foo;
foo const* pf2=::new(buff)foo{{3}};
或许也可以通过联合来实现。
显然,只需要查看 低于我链接的标准部分的 2 个项目。 Basic.life/10 告诉我们:
Creating a new object within the storage that a const complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
它附带了一个示例:
struct B {
B();
~B();
};
const B b;
void h() {
b.~B();
new (const_cast<B*>(&b)) const B; // undefined behavior
}
这最终让我得出一个结论,在真正 const
对象。因此,我认为问题中提到的注释(参考点8)是错误的——它应该排除所讨论的情况。