我可以通过placement new覆盖const对象吗



Basic.life/8告诉我们,我们可以使用对象占用的存储在其生命周期结束后创建一个新对象,并使用其原始名称引用它,除非:

  • 原始对象的类型不是const限定的,并且,如果是类类型,则不包含任何类型为const限定或引用类型的非静态数据成员,并且[…]

emphasis mine

但是,就在下面,我们可以看到一张纸条,上面写着:

  • 如果不满足这些条件,则可以通过调用std​::​launder从表示其存储地址的指针中获得指向新对象的指针

这解释了std::launder的用途。我们可以有一个具有const成员的类类型,并使用placementnew在那里创建一个具有不同内部值的新对象。

让我惊讶的是第一句话的第一部分。它清楚地表明,如果存储是const(不一定包含const成员,但整个对象被声明为const),我们不能按原样使用它来引用新对象,但这可能意味着std::launder可能会修复它。

但我们将如何创造这样的物体呢?最简单的例子失败了:

const auto x = 5;
new (&x) auto(10);

这是因为我们不能使用const void*作为放置new的缓冲区。我们可以const_cast它,但扔掉一个";真";constness是未定义的行为。我相信这里也是这样。

如果只是禁止使用const对象作为放置新缓冲区,我会理解,但如果是这样的话,第一句话中强调的部分的目的是什么?我们是否可以将一个真正的const对象的存储用于另一个对象?

显然,它所需要的只是查看在我链接的标准部分下方的2个项目。Basic.life/10告诉我们:

在具有静态、线程或自动存储持续时间的const完整对象所占用的存储中,或在此类const对象在其生存期结束前所占用的存储器中创建新对象,会导致未定义的行为。

它附带了一个例子:

struct B {
B();
~B();
};
const B b;
void h() {
b.~B();
new (const_cast<B*>(&b)) const B;     // undefined behavior
}

这最终使我得出结论,在由真正的const对象占用的内存上使用放置-new是非法的。因此,我认为问题中提到的说明(关于第8点)是错误的——它应该排除有关案件。

常量对象可以有指向其位置的非常量指针。

struct bar{ int x=5; };
struct foo{
const bar b;
};
foo f;
::new (&f) foo{{3}};

这里我有一个常量intf.b.x,我销毁它并构造一个值为3的新常量。

char buff[sizeof(foo)];
foo const* pf=::new(buff)foo;
foo const* pf2=::new(buff)foo{{3}};

也许也可以与工会合作。

最新更新