Quentin
Quentin

Reputation: 63124

Can an union's destructor be trivial?

Given the following code:

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

I wish to detach Foo::bar's lifetime from its enclosing Foo.
As far as I know, delete p; is well-defined if and only if Foo's destructor is trivial.
Since Foo only contains an union, and is not supposed to destruct its bar itself, it looks like it is the case: informally speaking, that destructor does nothing.

But is this code actually well-defined according to the standard?

See it live on Coliru

Upvotes: 3

Views: 684

Answers (1)

Pixelchemist
Pixelchemist

Reputation: 24936

First of all, [expr.delete] for me doesn't support your claim that "delete p; is well-defined if and only if Foo's destructor is trivial".

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.

Since the static type is FooBase whereas the dynamic type is Foo, I'd expect that to be UB.

Furthermore, (even in case this is not UB) I think that the destructor of Foo isn't trivial.

[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.

Thus Foo is a union-like class with bar (of type Bar which has a non-trivial destructor) being a variant member.

[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 [...].

Therefore, I think that the implicitly defaulted destructor of Foo should be defined as deleted and must be user-provided.

(Declare Foo q; and see the compilation fail.)

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.

I'd exepect that clause to extend to variant members as well since it doesn't say "non-variant data members".


You could have a virtual destructor in FooBase and then implement ~Foo() without including bar.~bar(); where it would be possible to destroy bar in advance without it being destructed again in ~Foo().

I'd however strongly advise against it since you'd have to somehow call ~bar for every static or dynamic Foo before it goes out of scope.

Upvotes: 2

Related Questions