I am trying to make a vector
that can hold string
and int
.
I've tried the code below, but I get the compilation error
error: use of deleted function 'my_union::~my_union()'
What am I doing wrong?
#include <iostream>
#include <vector>
using namespace std;
union my_union
{
string str;
int a;
};
int main()
{
vector<my_union> v;
my_union u; // error: use of deleted function 'my_union::~my_union()'
u.str = "foo";
v.push_back(u);
return 0;
}
In TypeScript, a union type variable is a variable which can store multiple type of values (i.e. number, string etc). A union type allows us to define a variable with multiple types. The union type variables are defined using the pipe ( '|' ) symbol between the types.
TypeScript - UnionTypeScript allows us to use more than one data type for a variable or a function parameter. This is called union type.
An intersection type combines multiple types into one. This allows you to add together existing types to get a single type that has all the features you need.
From here
If a union contains a non-static data member with a non-trivial special member function (default constructor, copy/move constructor, copy/move assignment, or destructor), that function is deleted by default in the union and needs to be defined explicitly by the programmer.
You must explicitly define a destructor for your union to replace the one automatically deleted for string
.
Also note that this is only valid in c++11. In earlier versions you can not have a type with non-trivial special member functions inside a union at all.
From a practical point of view, this may still not be a great idea.
When you create a union with a class that isn't basically plain old data, in C++11 it lets you. But it goes and implicitly deletes most of the special member functions like the destructor.
union my_union
{
string str;
int a;
};
the practical problem is that at the point of destruction C++ doesn't know which of the above parts of the union are valid.
You can work around this by using a tagged union, and keeping track which is active, and manually doing the destruction in that case.
So we can get something like:
struct tagged_union {
enum active {nothing, string, integer} which_active;
template<active...As>
using actives = std::integral_sequence<active, As...>
using my_actives = actives<nothing, string, integer>;
struct nothingness {};
union my_union
{
nothingness nothing;
std::string str;
int a;
~my_union() {};
} data;
using my_tuple = std::tuple<nothingness, std::string, int>;
template<active which>
using get_type = std::tuple_element_t<(std::size_t)which, my_tuple>;
template<class F>
void operate_on(F&& f) {
operate_on_internal(my_actives{}, std::forward<F>(f));
}
template<class T, class F>
decltype(auto) operate_on_by_type(F&& f) {
return std::forward<F>(f)(reinterpret_cast<T*>(&data));
}
// const versions go here
private:
// a small magic switch:
template<active...As, class F>
void operate_on_internal(actives<As...>, F&& f) {
using ptr = void(*)(my_union*,std::decay_t<F>*);
const ptr table[]={
[](my_union* self, std::decay_t<F>* pf){
std::forward<F>(*pf)(*(get_type<As>*)self);
}...,
nullptr
};
table[which](&data, std::address_of(f));
}
public:
template<class...Args>
tagged_union(Active w, Args&&...args) {
operate_on([&](auto& t){
using T = std::decay_t<decltype(t)>();
::new((void*)std::addressof(t)) T(std::forward<Args>(args)...);
which = w;
});
}
tagged_union():tagged_union(nothing){}
~tagged_union() {
operate_on([](auto& t){
using T = std::decay_t<decltype(t)>();
t->~T();
which=nothing;
::new((void*)std::addressof(t)) nothingness{}; // "leaks" but we don't care
});
}
};
which is basically a primitive sketch of how something like boost::variant
works if written in C++11.
It involves some heavy mojo.
The above has not been compiled, but the design is sound. Some nominally C++14 compilers don't like doing a pack expand around a full lambda, however, which would require even more boilerplate.
Before C++11 it was not allowed to use std::string
in a union as quoted here:
Unions cannot contain a non-static data member with a non-trivial special member function (copy constructor, copy-assignment operator, or destructor).
And since C++11 you can use std::string
in a union as already answered by @Rotem, you need to define a destructor explicitly for string or call the destructor explicitly
str.~basic_string<char>();
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