I am trying to compile the following tagged union in a c++ file and I am running into issues. Could someone explain what I am missing or what I have to change to get the following to work? I have tried looking this up online and unfortunately have gotten nowhere...
#include <string>
using std::string;
#include <iostream>
using std::cout;
using std::endl;
#include <new>
const int TU_STRING = 0;
const int TU_INT = 1;
const int TU_FLOAT = 2;
struct TU {
int type;
union {
int i;
float f;
std::string s;
} u;
TU(const TU& tu) : type(tu.type) {
switch (tu.type) {
case TU_STRING: new(&u.s)(tu.u.s); break;
case TU_INT: u.i = tu.u.i; break;
case TU_FLOAT: u.f = tu.u.f; break;
}
}
~TU() {
if (tu.type == TU_STRING)
u.s.~string();
}
};
int main() {
TU tu;
return 0;
}
I am compiling with clang using the following command
g++ -std=c++14 some.cpp
and I am getting lots of compiling errors
some.cpp:18:4: error: call to implicitly-deleted default constructor of 'union (anonymous union at some.cpp:12:4)'
TU(const TU& tu) : type(tu.type) {
^
some.cpp:15:18: note: default constructor of '' is implicitly deleted because variant field 's' has a non-trivial default constructor
std::string s;
^
some.cpp:18:4: error: attempt to use a deleted function
TU(const TU& tu) : type(tu.type) {
^
some.cpp:15:18: note: destructor of '' is implicitly deleted because variant field 's' has a non-trivial destructor
std::string s;
^
some.cpp:20:13: error: use of undeclared identifier 'TU_STRING'
case TU_STRING: new(&u.s)(tu.u.s); break;
^
some.cpp:20:34: error: unknown type name 'tu'
case TU_STRING: new(&u.s)(tu.u.s); break;
^
some.cpp:20:36: error: expected ')'
case TU_STRING: new(&u.s)(tu.u.s); break;
^
some.cpp:20:33: note: to match this '('
case TU_STRING: new(&u.s)(tu.u.s); break;
^
some.cpp:21:13: error: use of undeclared identifier 'TU_INT'
case TU_INT: u.i = tu.u.i; break;
^
some.cpp:22:13: error: use of undeclared identifier 'TU_FLOAT'
case TU_FLOAT: u.f = tu.u.f; break;
^
some.cpp:26:10: error: use of undeclared identifier 'tu'
if (tu.type == TU_STRING)
^
some.cpp:26:21: error: use of undeclared identifier 'TU_STRING'
if (tu.type == TU_STRING)
^
some.cpp:25:4: error: attempt to use a deleted function
~TU() {
^
some.cpp:15:18: note: destructor of '' is implicitly deleted because variant field 's' has a non-trivial destructor
std::string s;
^
some.cpp:32:8: error: no matching constructor for initialization of 'TU'
TU tu;
^
some.cpp:18:4: note: candidate constructor not viable: requires single argument 'tu', but no arguments were provided
TU(const TU& tu) : type(tu.type) {
^
11 errors generated.
Thanks!
EDIT: Updated code which still doesnt work
struct VariantType {
enum class Tag {INTEGER, STRING};
Tag tag;
union TypeUnion {
string inner_string;
int inner_int;
TypeUnion(const std::string& str) {
new(&inner_string) string(str);
}
TypeUnion(int inner_int_in) {
inner_int = inner_int_in;
}
TypeUnion(std::string other) : inner_string{std::move(other)} {}
} type_union;
VariantType(const std::string& str) : tag{Tag::STRING} {
new(&this->type_union.inner_string) string(str);
}
VariantType(int inner_int_in) : tag{Tag::INTEGER} {
this->type_union.inner_int = inner_int_in;
}
};
std::string
has a non-trivial constructor, so you need to write a constructor for the union
that performs a placement new
at s
's location.
Here is a version of your code that adds constructors to the union
. I don't think this is the best solution but it demonstrates what you need to do:
#include <iostream>
#include <new>
#include <string>
#include <utility>
const int TU_STRING = 0;
const int TU_INT = 1;
const int TU_FLOAT = 2;
struct TU {
union my_union {
struct i_type { int type; int i; } i;
struct f_type { int type; float f; } f;
struct s_type { int type; std::string s; } s;
my_union(int i) : i{TU_INT, i} {}
my_union(float f) : f{TU_FLOAT, f} {}
my_union(std::string s) : s{TU_STRING, std::move(s)} {}
my_union(my_union const& other) {
// This is safe.
switch (other.i.type) {
case TU_INT: ::new(&i) auto(other.i); break;
case TU_FLOAT: ::new(&f) auto(other.f); break;
case TU_STRING: ::new(&s) auto(other.s); break;
}
}
~my_union() {
// This is safe.
if (TU_STRING == s.type) {
s.~s_type();
}
}
} u;
TU(int i) : u(i) {}
TU(float f) : u(f) {}
TU(std::string s) : u(std::move(s)) {}
TU(TU const&) = default;
~TU() = default;
};
int main() {
TU tu("hello");
std::cout << tu.u.s.s << '\n';
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With