I'm working with a union which has a member that is a class that uses diamond inheritance, but the program encounters a segmentation fault upon assignment to this member.
My suspicion is that I need to add some copy constructors, but after multiple attempts, the correct way to do this still evades me.
I've included a minimal, reproducible example here.
struct Base
{
Base() : a(0) {}
Base(int x) : a(x) {}
int a;
};
struct Derived1 : virtual public Base
{
Derived1() {}
};
struct Derived2 : virtual public Base
{
Derived2() {}
};
struct Final : public Derived1, public Derived2
{
Final() {}
};
union Example
{
Final value;
int i;
};
int main()
{
Example example{ Final() };
example.i = -1;
/* Segfault on the line below.
* If the above line
example.i = -1;
* is removed, the segfault is not encountered. */
example.value = Final();
}
My thanks to anyone who knows how to do this.
Since the Base
class is virtual , most likely there are some internal control structures, which become destroyed, when you assign
example.i = -1;
When you recreate value
, eg
new(&example.value) Final();
example.value = Final();
before the assignment, the segmentation fault goes away. Although, I wouldn't recommend this hack.
As already mentioned in the comments, a std::variant
would be more appropriate, if you have C++17 available. The example would then become
std::variant<Final, int> example{ Final() };
example = -1;
example = Final();
Begin with c++11, unions with members that define their own constructors and/or copy-control members are allowed but When a union has members of built-in type, we can use ordinary assignment to change the value that the union holds, but Not for unions that have members of nontrivial class types. When we switch the union's value to and from a member of class type, we must construct or destroy that member. for example see bellow code
#include <iostream>
#include <new> // for placement new
class Type // non built in type with constructors
{
private:
int val;
public:
Type() : val{0} { }
explicit Type(int v) : val{v} { }
Type(const Type &obj) : val{obj.val} { }
int getval() { return val; }
};
class unionexample // I enclose union with class for readability
{
private:
enum { INT,TYPE } OBJ;
union
{
Type Tval; // non build in type
int ival; // build in type
};
public:
unionexample() : ival{0}, OBJ{INT} // default with int
{ }
unionexample(const unionexample &obj) : OBJ{obj.OBJ}
{
switch (obj.OBJ)
{
case INT:
this->ival = obj.ival;
break;
case TYPE:
new (&this->Tval) Type(obj.Tval);
break;
}
}
unionexample &operator=(int v)
{
if (OBJ == TYPE)
{
Tval.~Type(); // if it is TYPE destruct it
}
ival = v; // assign
OBJ = INT;
return *this;
}
unionexample &operator=(Type v)
{
if (OBJ == TYPE)
{
Tval = v; // if it is alderdy Type just assign
}
new (&Tval) Type(v); // else construct
OBJ = TYPE;
return *this;
}
void print()
{
switch (OBJ)
{
case INT:
std::cout << "ival = " << ival << std::endl;
break;
case TYPE:
std::cout << "Tval = " << Tval.getval() << std::endl;
break;
}
}
~unionexample()
{
if (OBJ == TYPE) // if it is TYPE we must destruct it
Tval.~Type();
}
};
int main()
{
unionexample ue;
ue.print();
ue = Type(1);
ue.print();
}
output:
ival = 0
Tval = 1
In Your case
int main()
{
Example example{ value : Final() }; // construct with Final
example.value.~Final(); // destruct Final
example.i = -1; // assign int built in type
new(&example.value) Final(); // construct
example.value.~Final(); // destruct finally
}
If you asked this question other than learning purpose you can use std::variant
as in other answer.
Thanks.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.