问题
I'm attempting to implement a tagged union.
My understanding was that in a C++ union, the non-trivial (i.e. not empty) destructors of non-static members are never called, thus we have to call them ourselves. That's what I did:
#include <iostream>
class C {
public:
C() {
std::cout << "C Ctor" << std::endl;
}
~C() {
std::cout << "C Dtor" << std::endl;
}
};
class B {
public:
B() {
std::cout << "B Ctor" << std::endl;
}
~B() {
std::cout << "B Dtor" << std::endl;
}
};
struct S {
int type;
union U {
C c;
B b;
U() {
}
~U() {}
} u;
S(int type) : type(type) {
if (type == 0) {
u.c = C();
} else {
u.b = B();
}
}
~S() {
if (type == 0) {
u.c.~C();
} else {
u.b.~B();
}
}
};
int main() {
S s(0);
return 0;
}
However, the output is:
C Ctor
C Dtor
C Dtor
Meaning, the C
destructor is being called twice, instead of just once.
What is going on? And if you notice additional issues with my tagged union implementation, please point them out.
回答1:
In
S(int type) : type(type) {
if (type == 0) {
u.c = C();
} else {
u.b = B();
}
}
Since you are in the body of the constrcutor, u.c = C();
is not initialization but is instead assigment. That means you see the constructor called for C()
, and then at the end of the expression the first destructor call is called to destroy that temporary. We can see this by adding
C& operator=(const C&) { std::cout << "operator=(const C&)\n"; return *this; }
to C
which changes the output to
C Ctor
operator=(const C&)
C Dtor
C Dtor
Then the second destructor call is when s
goes out of scope in main and its destructor is ran.
Do note that as is, the code has undefined behavior. Unions do not activate a member in the constructor user provided constructor you wrote so when you do
u.c = C();
you are assigning to an object that is not yet alive. You can't modify an object that isn't alive.
回答2:
In your constructor you create the temporary instance of C:
u.c = C();
which is copied and then destructed. So, first 2 lines of output belong to this instance. And the last output line is result of your ~S() call.
Besides this, beginning from C++17 you have standard powerful implementation of tagged union: https://en.cppreference.com/w/cpp/utility/variant
来源:https://stackoverflow.com/questions/62555368/destructor-of-union-member-seems-to-be-called-automatically