How to write a destructor for union-like type

I'm trying to use union (C ++), which has some non-primitive variables, but I'm stuck trying to create a destructor for this class. As I already read, it is impossible to guess which variable is used in the union, so there is no implicit destructor, and since I use this union on the stack, there are compiler errors that the destructor destroys. The union is as follows:

struct LuaVariant { LuaVariant() : type(VARIANT_NONE) { } LuaVariantType_t type; union { std::string text; Position pos; uint32_t number; }; }; 

The type variable contains which union field is used (selected from the enumeration) for reading from the union, and it can be used to determine which value should be removed. I tried different approaches, but none of them worked. First of all, I just tried the default destructor:

 ~LuaVariant() = default; 

This did not work because the default value ... was deleted. So, I tried to replace the value with an empty one, so that the contents were erased, and there would be no problem of "leak" of an empty value:

 ~LuaVariant() { switch (type) { case VARIANT_POSITION: case VARIANT_TARGETPOSITION: { Position p; std::swap(p, pos); break; } case VARIANT_STRING: { std::string s; std::swap(s, text); break; } default: number = 0; break; } }; 

But since I am not a master of trade unions, I don’t know if this can cause other problems, such as allocated memory that is never freed, or something like that. Is it possible to use this swap strategy without errors and problems?

+5
source share
2 answers

This grouping (union + enum value for a recognizing type) is called discriminatory union.

You will need to name any construction / destruction, because the union itself cannot (if it could, it could also distinguish between initialized / uninitialized types in the union, and you would not need an enumeration).

code:

 class LuaVariant // no public access to the raw union { public: LuaVariant() : type(VARIANT_NONE) { } ~LuaVariant() { destroy_value(); } void text(std::string value) // here a setter example { using std::string; destroy_value(); type = VARIANT_TEXT; new (&value.text) string{ std::move(value) }; } private: void destroy_value() { using std::string; switch(type) { case VARIANT_TEXT: (&value.text)->string::~string(); break; case VARIANT_POSITION: (&value.pos)->Position::~Position(); break; case VARIANT_NUMBER: value.number = 0; break; default: break; } } LuaVariantType_t type; union { std::string text; Position pos; uint32_t number; } value; }; 
+6
source

If you want to use std::string in a union in C ++ 11, you must explicitly call its destructor and post a new one to create it. Example from cppreference.com :

 #include <iostream> #include <string> #include <vector> union S { std::string str; std::vector<int> vec; ~S() {} // needs to know which member is active, only possible in union-like class }; // the whole union occupies max(sizeof(string), sizeof(vector<int>)) int main() { S s = {"Hello, world"}; // at this point, reading from s.vec is UB std::cout << "s.str = " << s.str << '\n'; s.str.~basic_string<char>(); new (&s.vec) std::vector<int>; // now, s.vec is the active member of the union s.vec.push_back(10); std::cout << s.vec.size() << '\n'; s.vec.~vector<int>(); } 
+2
source

All Articles