Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Union usage

Tags:

c++

unions

So far I have just used unions to store either member A or member B.
I do now find myself in the case where I want to change the used member during runtime.

union NextGen {
    std::shared_ptr<TreeRecord> Child = nullptr;
    std::vector<std::shared_ptr<TreeRecord>> Children;
};

My current usage:

void TreeRecord::AddChild(const std::shared_ptr<TreeRecord>& NewChild) {
    if(_childCount == 0) {
        _nextGeneration.Child = NewChild;
        _childCount++;
    } else if(_childCount == 1) {
        //This is not clear to me:
        //Do I have to set Child to nullptr first?
        //Do I need to clear the Children vecor?
        //Or will it work like this?
        _nextGeneration.Children.push_back(_nextGeneration.Child);
        _nextGeneration.Children.push_back(NewChild);
        _childCount++;
    } else {
        _nextGeneration.Children.push_back(NewChild);
        _childCount++;
    }
}

New implementation (try):

typedef std::shared_ptr<TreeRecord> singlechild_type;
typedef std::vector<std::shared_ptr<TreeRecord>> children_type;



union {
    singlechild_type _child;
    children_type _children;
};



void TreeRecord::AddChild(const singlechild_type& NewChild) {
    if(_childCount == 0) {
        _child = NewChild;
        _childCount = 1;

    } else if(_childCount == 1) {
        singlechild_type currentChild = _child; //Copy pointer
        _child.~singlechild_type(); //Destruct old union member
        new (&_children) children_type(); //Construct new union member
        _children.push_back(currentChild); //Add old child to vector
        _children.push_back(NewChild); //Add new child to vector
        _childCount = 2;

    } else {
        _children.push_back(NewChild);
        _childCount++;
    }
}
like image 459
Noel Widmer Avatar asked Jan 03 '16 16:01

Noel Widmer


People also ask

What are C unions used for?

Union in C is a special data type available in C that allows storing different data types in the same memory location. You can define a union with many members, but only one member can contain a value at any given time. Unions provide an efficient way of using the same memory location for multiple purposes.

What is real time example of union in C?

Example of Union in Cfloat salary; }; union detail{ int id; char name[30]; float salary; }; union detail{ int id; char name[30]; float salary; }; In the above, we have defined an union type named detail which has three members of different data types such as integer, character and float.

What is the advantage and disadvantage of union in C?

It occupies less memory compared to structure. When you use union, only the last variable can be directly accessed. Union is used when you have to use the same memory location for two or more data members. It enables you to hold data of only one data member.

Why do we use union inside structures in C?

A union is an object similar to a structure except that all of its members start at the same location in memory. A union variable can represent the value of only one of its members at a time. In C++, structures and unions are the same as classes except that their members and inheritance are public by default.


2 Answers

You need a C++11 compliant compiler. Read about union-s.

In general, you need to explicitly call the destructor of the old union member, and then the constructor of the new union member. Actually, you'll better have tagged unions, with the actual union being anonymous and member of some class:

class TreeRecord;

class TreeRecord {
   bool hassinglechild;
   typedef std::shared_ptr<TreeRecord> singlechild_type;
   typedef std::vector<std::shared_ptr<TreeRecord>> children_type;
   union {
     singlechild_type child;  // when hassinglechild is true
     children_type children;  // when hassinglechild is false
   }
   TreeRecord() : hassinglechild(true), child(nullptr) {};
   void set_child(TreeRecord&ch) {
     if (!hassinglechild) {
       children.~children_type();
       hassinglechild = true;
       new (&child) singlechild_type(nullptr);
     };
     child = ch;
   }
   /// other constructors and destructors skipped
   /// more code needed, per rule of five
}

Notice that I am explicitly calling the destructor ~children_type() then I am using the placement new to explicitly call the constructor for child.

Don't forget to follow the rule of five. So you need more code above

See also boost::variant

BTW your code is suggesting that you distinguish the case when you have a child and the case when you have a one-element vector of children. Is that voluntary and meaningful?

PS. In some languages, notably Ocaml, tagged unions (a.k.a. sum types) are considerably easier to define and implement than in C++11.... See wikipage of algebraic data types.

like image 100
Basile Starynkevitch Avatar answered Oct 21 '22 15:10

Basile Starynkevitch


Note, using more than one element of a union concurrently invokes undefined behavior, e.g.

    _nextGeneration.Children.push_back(_nextGeneration.Child);

As @BasileStarynkevitch mentioned, one way is to avoid this is a tagged union.

like image 44
Jason Avatar answered Oct 21 '22 16:10

Jason