在初始化内存中使用new放置合法吗? [英] Is it legal to use placement new on initialised memory?

查看:99
本文介绍了在初始化内存中使用new放置合法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在探索在C ++中实现真正的(部分)不可变数据结构的可能性.由于C ++似乎无法区分变量和变量存储的对象,因此真正替换对象的唯一方法(无需赋值操作!)是使用new放置:

I am exploring the possibility of implementing true (partially) immutable data structures in C++. As C++ does not seem to distinguish between a variable and the object that variable stores, the only way to truly replace the object (without assignment operation!) is to use placement new:

auto var = Immutable(state0);
// the following is illegal as it requires assignment to
// an immutable object
var = Immutable(state1);
// however, the following would work as it constructs a new object
// in place of the old one
new (&var) Immutable(state1);

假设没有非平凡的析构函数可以运行,那么在C ++中这合法吗?还是我应该期待未定义的行为?如果它取决于标准,那是我可以预期的最小/最大标准版本?

Assuming that there is no non-trivial destructor to run, is this legal in C++ or should I expect undefined behaviour? If its standard-dependant, which is the minimal/maximal standard version where I can expect this to work?

附录:由于似乎人们在2019年仍会阅读此内容,因此请快速注意一下-在现代(后17)C ++中,使用

Addendum: since it seems people still read this in 2019, a quick note — this pattern is actually legally possible in modern (post 17) C++ using std::launder().

推荐答案

您写的内容在技术上是合法的,但几乎肯定没有用.

What you wrote is technically legal but almost certainly useless.

假设

struct Immutable {
  const int x;
  Immutable(int val):x(val) {}
};

我们真正简单的不可变类型.

for our really simple immutable type.

auto var = Immutable(0);
::new (&var) Immutable(1);

这是完全合法的.

而且没有用,因为您不能使用var来引用放置在new之后的Immutable(1)的状态.任何此类访问都是未定义的行为.

And useless, because you cannot use var to refer to the state of the Immutable(1) you stored within it after the placement new. Any such access is undefined behavior.

您可以执行以下操作:

auto var = Immutable(0);
auto* pvar1 = ::new (&var) Immutable(1);

,并且访问*pvar1是合法的.您甚至可以:

and access to *pvar1 is legal. You can even do:

auto var = Immutable(0);
auto& var1 = *(::new (&var) Immutable(1));

但在任何情况下都不能在将var放置在其上后再引用它.

but under no circumstance may you ever refer to var after you placement new'd over it.

实际const数据是对编译器的保证,您永远都不会更改该值.与引用const或指向const的指针相比,这只是建议您不要修改数据.

Actual const data in C++ is a promise to the compiler that you'll never, ever change the value. This is in comparison to references to const or pointers to const, which is just a suggestion that you won't modify the data.

声明为const的结构成员是实际上是const".编译器将假定它们永远不会被修改,并且不会费力去证明这一点.

Members of structures declared const are "actually const". The compiler will presume they are never modified, and won't bother to prove it.

您在实际上是一个旧实例的位置创建一个新实例违反了这一假设.

You creating a new instance in the spot where an old one was in effect violates this assumption.

您可以执行此操作,但是您不能使用旧名称或指针来引用它. C ++让您脚踏实地.前进吧,我们敢!

You are permitted to do this, but you cannot use the old names or pointers to refer to it. C++ lets you shoot yourself in the foot. Go right ahead, we dare you.

这就是为什么这种技术合法,但几乎完全无用的原因.一个具有静态单一分配的优秀优化器已经知道您将停止使用var并创建

This is why this technique is legal, but almost completely useless. A good optimizer with static single assignment already knows that you would stop using var at that point, and creating

auto var1 = Immutable(1);

它可以很好地重用存储.

it could very well reuse the storage.

通常将在另一个变量之上的提示放置行为定义为行为.通常这是一个坏主意,并且易碎.

Caling placement new on top of another variable is usually defined behaviour. It is usually a bad idea, and it is fragile.

这样做将结束旧对象的生命周期,而无需调用析构函数.如果存在某些特定假设(类型完全相同,没有const问题),则指向旧对象的引用和指针以及旧对象的名称将引用新对象.

Doing so ends the lifetime of the old object without calling the destructor. References and pointers to and the name of the old object refer to the new one if some specific assumptions hold (exact same type, no const problems).

修改声明为const的数据或包含const字段的类,会在引脚掉下导致未定义的行为.这包括结束声明为const的自动存储字段的生命周期,并在该位置创建一个新对象.旧名称,指针和引用不安全使用.

Modifying data declared const, or a class containing const fields, results in undefined behaviour at the drop of a pin. This includes ending the lifetime of an automatic storage field declared const and creating a new object at that location. The old names and pointers and references are not safe to use.

如果在对象的生存期结束之后并且在该对象占用的存储空间被重用之前,或者 释放后,将在原始对象占用的存储位置创建一个新对象,该指针将 指向原始对象,指向原始对象的引用或原始名称 对象将自动引用新对象,并且一旦新对象的生命周期开始,就可以 用于处理新对象,如果:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • (8.1) 新对象的存储空间正好覆盖了原始对象所占据的存储位置, 和

  • (8.1) the storage for the new object exactly overlays the storage location which the original object occupied, and

(8.2) 新对象与原始对象具有相同的类型(忽略顶级cv限定词),并且

(8.2) the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

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

(8.3) 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

(8.4) 原始对象是类型最大的派生对象(1.8) Ť 而新对象是最派生的 类型的对象 Ť (也就是说,它们不是基类的子对象).

(8.4) the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).

简而言之,如果您的不变性是通过const成员编码的,则使用旧名称​​或指向旧​​内容的指针是未定义的行为.

In short, if your immutability is encoded via const members, using the old name or pointers to the old content is undefined behavior.

您可以使用place new的返回值来引用新对象,而不能使用其他任何方法.

You may use the return value of placement new to refer to the new object, and nothing else.

异常的可能性使得阻止执行未定义行为或必须摘要退出的代码变得极为困难.

Exception possibilities make it extremely difficult to prevent code that exdcutes undefined behaviour or has to summarially exit.

如果要引用语义,请使用指向const对象或可选const对象的智能指针.两者都处理对象的生存期.第一个需要堆分配,但允许移动(并可能共享引用),第二个允许自动存储.两者都将手动对象生命周期管理从业务逻辑中移出.现在,两者都是可以为空的,但是要避免强健地进行操作,无论如何都很难手动进行.

If you want reference semantics, either use a smart pointer to a const object or an optional const object. Both handle object lifetime. The first requires heap allocation but permits move (and possibly shared references), the second permits automatic storage. Both move manual object lifetime management out of business logic. Now, both are nullable, but avoiding that robustly is difficult doing it manually anyhow.

还考虑在写指针上进行复制,以允许出于效率目的而对逻辑const数据进行突变.

Also consider copy on write pointers that permit logically const data with mutation for efficiency purposes.

这篇关于在初始化内存中使用new放置合法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆