5

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

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

强调我的

但是,就在下面,我们可以看到一条注释说:

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

这解释std::launder. 我们可以有一个具有const成员的类类型,并使用placement-new 在其中创建一个具有不同内部值的新对象。

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

但是我们如何创建这样的对象呢?最简单的例子失败了:

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

这是因为我们不能const void*用作placement-new 的缓冲区。我们可以const_cast做到,但抛弃“真实”const是未定义的行为。我相信这里也是如此。

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

4

2 回答 2

1

显然,只需要在我链接到的标准部分下方查看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
}

这最终使我得出一个结论,即在真正对象占用的内存上使用放置newconst是非法的。因此,我认为问题中提到的注释(参考第8点)是错误的 - 它应该排除有问题的情况。

于 2021-09-23T17:13:06.933 回答
-1

一个 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}};

也许也可以通过工会来做到这一点。

于 2021-09-21T23:08:32.610 回答