Unions in C++?

im very fail to understand why unions are good for
Ive nevery used unions or come across unions in codes, the whole concept of shared memory in unions is really dangerous, it can cause corruption of memory if not used properly, unions were used in old times when memory was really really expensive!!!!
Say you are going to need an int or you are going to need a double (but never both at the same time) depending on some other variables. Instead of wasting memory for both an int and a double you can use a union that will only take up as much memory that is needed to store the largest of the two types.
anirudh sn wrote:
Ive nevery used unions or come across unions in codes, the whole concept of shared memory in unions is really dangerous, it can cause corruption of memory if not used properly, unions were used in old times when memory was really really expensive!!!!

An example of union in real life code, SDL_Event [General event structure]
http://hg.libsdl.org/SDL/file/b4b0e9f1669c/include/SDL_events.h#l420

The benefits: A single data type to handle all the event types without wasting memory unnecessarily.


we use VARIANTs at work for populating our datagrids. these are unions under the hood. like peter said it means i dont have to worry about methods with different arguments to pass in depending on variable type. i just pass in a variant type.
@GreyWolf, apart from critical memory intensive applications wherein the memory is really scarse, does it make sense to use unions anywhere?? especially in normally application programming, arent there high chances of memory corruption if used wrongly??
apart from critical memory intensive applications wherein the memory is really scarse, does it make sense to use unions anywhere??

Did you follow the link in his post? SFML also uses something similar for event handling.


especially in normally application programming, arent there high chances of memory corruption if used wrongly??

That can be said of most C++ constructs. It's quite easy to shoot yourself in the foot.
It may be relevant for interpreting the data contained in a file. For example a JPEG image from a digital camera contains EXIF information, detailing the camera settings used etc. Or image files such as RAW or TIFF, when you start to decode them, a union may be used to represent a portion of data which needs to be interpreted in one of several different ways.
anirudh sn wrote:
apart from critical memory intensive applications wherein the memory is really scarse, does it make sense to use unions anywhere??
Its use is more about being efficient and efficiency bring benefits in all sorts of areas. Do you really want a data type where 90% of it is always junk, or 10 data types to handle instead of one?
It was useful for providing a kind of crude subclassing of structs in C code. I've seen code that did something like:

1
2
3
4
5
6
7
8
9
10
struct BaseType
{
  int type;
  float baseData;
  union
  {
    struct SpecialisedTypeAData;
    struct SpecialisedTypeBData;
  }
};


So baseData corresponds to "base class" data, i.e. data that was common to all of the specialised types, and the union holds the "derived class" data - i.e. data relating only to one particular specialised type.

I've only ever seen this in C code, or code that was copied and pasted from C into C++ without properly refactoring it.
Its use is more about being efficient and efficiency bring benefits in all sorts of areas. Do you really want a data type where 90% of it is always junk, or 10 data types to handle instead of one?

Unless I was working in an environment where memory was very limited, I would want a data type that is clearly understandable and maintainable, with as little chance for confusion or introduction of errors as possible.

I wouldn't compromise that for the sake of using less memory, unless it was absolutely necessary.
> apart from critical memory intensive applications wherein the memory is really scarse,
> does it make sense to use unions anywhere??

I idea of a union exists primarily because the language is statically typed, are there are situations where we need to deal with a truly heterogeneous set of types. It is rarely used for scrounging memory.


> especially in normally application
> programming, arent there high chances of memory corruption if used wrongly??

In well-written C++ code, we almost always find that the unions used are type-safe discriminated unions - what the IS refers to as a 'union like class'.

This is the general idea:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <string>
#include <new>
#include <memory>

struct int_or_str
{
    bool is_int() const { return is_int_ ; }
    bool is_str() const { return !is_int() ; }

    ~int_or_str() { if( is_str() ) str.std::string::~string() ; }

    int_or_str( int v = 0 ) : i(v) {}
    int_or_str( const std::string& s ) : is_int_(false) { ::new ( std::addressof(str) ) std::string(s) ; }
    int_or_str( std::string&& s ) : is_int_(false) { ::new ( std::addressof(str) ) std::string( std::move(s) ) ; }
    int_or_str( const char* cstr ) : is_int_(false) { ::new ( std::addressof(str) ) std::string(cstr) ; }

    int_or_str( const int_or_str& that ) : is_int_( that.is_int_)
    {
        if( that.is_int() ) i = that.i ;
        else ::new ( std::addressof(str) ) std::string( that.str ) ;
    }

    int_or_str( int_or_str&& that ) : is_int_( that.is_int_)
    {
        if( that.is_int() ) i = that.i ;
        else ::new ( std::addressof(str) ) std::string( std::move(that.str) ) ;
    }

    int_or_str& operator= ( const int_or_str& that )
    {
        if( this != std::addressof(that) )
        {
            if( is_str() && that.is_str() ) str = that.str ;
            else
            {
                if( is_str() ) str.std::string::~string() ;
                is_int_ = that.is_int() ;
                if( is_int() ) i = that.i ;
                else ::new ( std::addressof(str) ) std::string( that.str ) ;
            }
        }
        return *this ;
    }

    int_or_str& operator= ( int_or_str&& that )
    {
        if( this != std::addressof(that) )
        {
            if( is_str() && that.is_str() ) str = std::move(that.str) ;
            else
            {
                if( is_str() ) str.std::string::~string() ;
                is_int_ = that.is_int() ;
                if( is_int() ) i = that.i ;
                else ::new ( std::addressof(str) ) std::string( std::move(that.str) ) ;
            }
        }
        return *this ;
    }

    /*explicit*/ operator int() const { if( !is_int() ) throw "whatever" ; return i ; }
    /*explicit*/ operator int&() { if( !is_int() ) throw "whatever" ; return i ; }
    /*explicit*/ operator const std::string&() const { if( is_int() ) throw "whatever" ; return str ; }
    /*explicit*/ operator std::string&() { if( is_int() ) throw "whatever" ; return str ; }

    private:

        bool is_int_ = true ; // discriminant

        union // anonymous union
        {
            std::string str ;
            int i ;
        };
};

std::ostream& operator<< ( std::ostream& stm, const int_or_str& v )
{
    if( v.is_int() ) return stm << int(v) ;
    else return stm << std::string(v) ;
}


We can use the union like class it in a completely type safe manner. In practice, we would just use a library; for instance boost::any or boost::variant

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{
    {
        int_or_str v = "hello" ;
        std::cout << v << ' ' ;
        std::string str = v ;
        v = 123 ;
        std::cout << v << ' ' ;
        v = str ;
        std::cout << v << '\n' ;
    }

    {
        // in practice, we use a library
        // #include <boost/variant.hpp>
        boost::variant< std::string, int > v = "hello" ;
        std::cout << v << ' ' ;
        std::string str = boost::get<std::string>(v) ;
        v = 123 ;
        std::cout << v << ' ' ;
        v = str ;
        std::cout << v << '\n' ;
    }
}
Topic archived. No new replies allowed.