在封闭对象的生命周期中,通过新的放置重用数据成员存储 [英] Reusing data member storage via placement new during enclosing object's lifetime

查看:91
本文介绍了在封闭对象的生命周期中,通过新的放置重用数据成员存储的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是对我的上一个问题我似乎使问题比我最初打算的要复杂得多. (请参阅有问题的讨论并在此处回答评论.) 该问题是对原始问题的略微修改,消除了在构建/破坏封闭对象期间的特殊规则问题.

This is a follow-up to my previous question where I seem to have made the problem more involved than I had originally intended. (See discussions in question and answer comments there.) This question is a slight modification of the original question removing the issue of special rules during construction/destruction of the enclosing object.

是否允许在其封闭对象的生存期内重用非静态数据成员的存储,如果是,则在什么条件下?

Is it allowed to reuse storage of a non-static data member during the lifetime of its enclosing object and if so under what conditions?

考虑程序

#include<new>
#include<type_traits>

using T = /*some type*/;
using U = /*some type*/;

static_assert(std::is_object_v<T>);
static_assert(std::is_object_v<U>);
static_assert(sizeof(U) <= sizeof(T));
static_assert(alignof(U) <= alignof(T));

struct A {
    T t /*initializer*/;
    U* u;

    void construct() {
        t.~T();
        u = ::new(static_cast<void*>(&t)) U /*initializer*/;
    }

    void destruct() {
        u->~U();
        ::new(static_cast<void*>(&t)) T /*initializer*/;
    }

    A() = default;
    A(const A&) = delete;
    A(A&&) = delete;
    A& operator=(const A&) = delete;
    A& operator=(A&&) = delete;
};

int main() {
    auto a = new A;
    a->construct();
    *(a->u) = /*some assignment*/;
    a->destruct(); /*optional*/
    delete a; /*optional*/

    A b; /*alternative*/
    b.construct(); /*alternative*/
    *(b.u) = /*some assignment*/; /*alternative*/
    b.destruct(); /*alternative*/
}

除了static_assert之外,还假定TU的初始化程序,析构函数和赋值不抛出.

Aside from the static_asserts assume that the initializers, destructors and assignments of T and U do not throw.

对象类型TU还需要满足哪些条件,以便程序定义行为(如果有的话)?

What conditions do object types T and U need to satisfy additionally, so that the program has defined behavior, if any?

它是否取决于实际调用的A的析构函数(例如,取决于是否存在/*optional*//*alternative*/行)?

Does it depend on the destructor of A actually being called (e.g. on whether the /*optional*/ or /*alternative*/ lines are present)?.

它是否取决于A的存储持续时间,例如是否使用main中的/*alternative*/行?

Does it depend on the storage duration of A, e.g. whether /*alternative*/ lines in main are used instead?

请注意,除析构函数和destruct函数外,该程序在新放置后不使用t成员.当然,不允许在存储空间被其他类型占用的情况下使用它.

Note that the program does not use the t member after the placement-new, except in the destructor and the destruct function. Of course using it while its storage is occupied by a different type is not allowed.

还请注意,由于我不允许TU引发异常,因此该程序在所有执行路径中调用其析构函数之前,先在t中构造原始类型的对象.

Also note that the program constructs an object of the original type in t before its destructor is called in all execution paths since I disallowed T and U to throw exceptions.

也请注意,我不鼓励任何人编写这样的代码.我的目的是更好地理解语言的细节.特别是,至少在不调用析构函数的情况下,我没有发现任何禁止此类新闻的消息.

Please also note that I do not encourage anyone to write code like that. My intention is to understand details of the language better. In particular I did not find anything forbidding such placement-news as long as the destructor is not called, at least.

推荐答案

此答案基于我们可以尝试(通过检查每个条件)将我决定调用不死对象"子句的内容应用于以前存在的任何先前对象,此处将其应用于成员T类型的t:

We can try to apply (by checking each condition) what I've decided to call the "undead object" clause to any previous object that used to exist, here we apply it to the member t of type T:

生存时间 [basic.life ]/8

如果在对象的生存期结束之后且在存储之前 被占用的对象被重用或释放,一个新的对象是 在原始对象占用的存储位置创建的 指向原始对象的指针,引用的引用 到原始对象,或者原始对象的名称将 自动引用新对象,并且 新对象已启动,可以用于操作新对象,如果:

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限定的,也不是此类对象的子对象,并且

(8.3) the original object is neither a complete object that is const-qualified nor a subobject of such an object, and

(8.4)原始对象和新对象都不是 可能重叠的子对象([intro.object]).

(8.4) neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).

条件1和2 可通过在旧成员上使用新的展示位置来自动保证:

Conditions 1 and 2 are automatically guaranteed by the use of placement new on the old member:

struct A {
    T t /*initializer*/; (...)

    void destruct() { (...)
        ::new(static_cast<void*>(&t)) T /*initializer*/;
    }

位置相同,类型相同.这两个条件都易于验证.

The location is the same and the type is the same. Both conditions are easily verified.

均未创建A个对象:

auto a = new A;
...
A b; /*alternative*/

是const限定的完整对象,因此t不是const限定的完整对象的成员. 满足条件3 .

are const qualified complete objects so t isn't a member of a const qualified complete object. Condition 3 is met.

现在,潜在重叠的定义在对象模型 [对象对象]/7 :

Now the definition of potentially-overlapping is in Object model [intro.object]/7:

可能重叠的子对象是:

A potentially-overlapping subobject is either:

(7.1)一个基类子对象,或者

(7.1) a base class subobject, or

(7.2)用no_unique_address声明的非静态数据成员 属性.

(7.2) a non-static data member declared with the no_­unique_­address attribute.

t成员都不是,并且满足条件4 .

The t member is neither and condition 4 is met.

满足所有4个条件,因此成员名称t可用于命名新对象.

All 4 conditions are met so the member name t can be used to name the new object.

[注意,在任何时候我都没有提到子对象不是其子对象的const成员这一事实.那不是最新草案的一部分.

[Note that at no point I even mentioned the fact the subobject isn't a const member not its subobjects. That isn't part of the latest draft.

这意味着const子对象可以合法地更改其值,并且引用成员可以更改其对现有对象的引用.这不仅令人不安,而且许多编译器可能不支持.尾注.]

It means that a const sub object can legally have its value changed, and a reference member can have its referent changed for an existing object. This is more than unsettling and probably not supported by many compilers. End note.]

这篇关于在封闭对象的生命周期中,通过新的放置重用数据成员存储的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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