Is it correct to use std::string (or other non trivial types) with int (or other trivial and non trivial types) inside the same union?
I am already implemented it like this:
#include <iostream>
#include <string>
struct Foo
{
enum Type {data_string, data_int};
int m_type;
Foo(Type t) : m_type(t)
{
if (t == Type::data_string) {
new (&s) std::string();
}
}
~Foo()
{
if (m_type == Type::data_string) {
s.~basic_string();
}
}
union
{
int n;
std::string s;
};
};
int main()
{
Foo f1(Foo::Type::data_string);
f1.s = "hello ";
std::cout << f1.s;
Foo f2(Foo::Type::data_int);
f2.n = 100;
std::cout << f2.n;
}
and it works pretty good. But i am not sure about this code. Is it correct code from the C++ standard perspective?
>Solution :
You should not use a union with non trivial types.
A union is not meant to deal with proper construction and detruction of C++ objects in it.
You took care of it manually in your code, which is technically correct, but very much error prone. You can easily get into UB land if you construct or destruct the wrong type in the union.
In general in C++ is it advised to use std::variant for a generic sum-type / discriminated union (requires #include <variant>).
From the documentation:
The class template std::variant represents a type-safe union.
So instead of your union I recommend to use something like:
std::variant<int, std::string> m_value;
This way your Foo constructor and destructor can be defaulted. The variant will take care of proper construction and destruction of the std::string (or any other non-trivial type in it).