联合的析构函数可以是微不足道的吗?

Can an union's destructor be trivial?

给定以下代码:

struct Bar {
    Bar() { }
    ~Bar() { }
};

struct FooBase {
    // No virtual destructor
};

struct Foo : FooBase {
    Foo() : bar{} { }

    union {
        Bar bar;
    };
};

int main() {
    FooBase *p = new Foo;
    static_cast<Foo *>(p)->bar.~Bar();
    delete p;
}

我希望将 Foo::bar 的生命周期与其封闭的 Foo 分开。
据我所知,delete p; 是明确定义的当且仅当 Foo 的析构函数是微不足道的。
由于 Foo 只包含一个联合,并且不应该破坏它的 bar 本身,看起来是这样的:非正式地说,析构函数什么都不做。

但是这个代码实际上是按照标准定义好的吗?

See it live on Coliru

首先,[expr.delete] 对我来说不支持你的说法“delete p; 是明确定义的当且仅当 Foo 的析构函数是微不足道的".

In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.

由于静态类型是 FooBase 而动态类型是 Foo,我希望它是 UB。

此外,(即使这不是 UB)我认为 Foo 的析构函数不是微不足道的。

[class.union]

A union-like class is a union or a class that has an anonymous union as a direct member. A union-like class X has a set of variant members. If X is a union its variant members are the non-static data members; otherwise, its variant members are the non-static data members of all anonymous unions that are members of X.

因此 Foo 是一个类似联合的 class,其中 bar(类型 Bar 具有非平凡的析构函数)是变体成员。

[class.dtor]

4) If a class has no user-declared destructor, a destructor is implicitly declared as defaulted [...].

5) A defaulted destructor for a class X is defined as deleted if

  • X is a union-like class that has a variant member with a non-trivial destructor [...].

因此,我认为Foo的隐式默认析构函数应该定义为已删除并且必须由用户提供。

(声明Foo q;并看到编译失败。)

A destructor is trivial if it is not user-provided and if

  • [...] for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.

我希望该条款也扩展到变体成员,因为它没有说 "non-variant data members"。


可以 FooBase 中有一个虚拟析构函数,然后实现 ~Foo() 而不包括 bar.~bar(); 可以销毁 bar 提前,而不会在 ~Foo().

中再次被破坏

但是我强烈反对它,因为在它超出范围之前你必须以某种方式为每个静态或动态 Foo 调用 ~bar