[ACCEPTED]-Is it a good practice to use unions in C++?-c++

Accepted answer
Score: 25

Unions can be fine, as long as you use them 27 carefully.

They can be used in two ways:

  1. To allow a 26 single type of data to be accessed in several 25 ways (as in your example, accessing a colour 24 as an int or (as you probably intended) four 23 chars)

  2. To make a polymorphic type (a single 22 value that could hold an int or a float 21 for example).

Case (1) Is fine because you're 20 not changing the meaning of the type - you 19 can read and write any of the members of 18 the union without breaking anything. This 17 makes it a very convenient and efficient 16 way of accessing the same data in slightly 15 different forms.

Case (2) can be useful, but 14 is extremely dangerous because you need 13 to always access the right type of data 12 from within the union. If you write an int 11 and try to read it back as a float, you'll 10 get a meaningless value. Unless memory usage 9 is your primary consideration it might be 8 better to use a simple struct with two members 7 in it.

Unions used to be vital in C. In C++ there 6 are usually much nicer ways to achieve the 5 same ends (e.g. a class can be used to wrap 4 a value and allow it to be accessed in different 3 ways). However, if you need raw performance 2 or have a critical memory situation, unions 1 may still be a useful approach.

Score: 8

Is it good practice? Yes, but with some 12 caveats.

Back in the days when memory was 11 scarce, unions were popular to re-use memory. Those 10 days are long gone and using unions for 9 that purpose adds needless complexity. Don't 8 do it.

If a union genuinely describes your 7 data, as it does in the example you give, then 6 it is a perfectly reasonable thing to do. However, be 5 warned that you are building in some platform 4 dependencies. On a different platform with 3 different integer sizes or different byte 2 ordering you might not get what you were 1 expecting.

Score: 6

In C++ the use of unions is constrained 4 by the fact that that their members must 3 be PODs (plain old data). For example, a 2 union member cannot have a constructor or 1 a destructor, among other restrictions.

Score: 3

In my hypothetical C++ Coding Standards 10 unions would be banned, since they tend violate 9 the "correctness, simplicity and clarity 8 come first" rule.

However, this is not 7 the widespread recommendation, and Sutter 6 and Alexandrescu didn't rule against them 5 in their C++ Coding Standards, as far as I remember.

Fortunately, everybody 4 I know finds them so hard to get right that 3 they don't produce them. If only they had 2 found void *'s in APIs hard to get right, too 1 :)

Score: 3

This code would undefined behaviour if used 7 the way you describe.

In C++, only the last-written 6 member of a union is active at any one time. Accessing 5 other members is like accessing uninitailized 4 variables.

For in-depth discussion see this thread.

Unions 3 maybe be used for saving space; e.g. implementing 2 a Variant. They may not be used for type 1 punning.

Score: 2

Using unions is still acceptable practice. Just 9 change rgbBytes to an array :)

In C, unions 8 can be used for different purposes. Sometimes 7 they are used as a Variant type, i.e. to 6 hold values of different type in the same 5 memory location. This usage would be questionable 4 in C++, because you'd use inheritance/polymorphism. However, the 3 other use of unions is to provide different 2 "interface" to the same data. This kind 1 of usage is still valid for C++.

Score: 2

Since you are using C++, I'd say that it's 8 not good practice. If you are limited to 7 pure C, sure why not.

The biggest problem 6 imo is that the size of the union is always 5 the size of the largest "member", so if 4 you want to store a byte or a shitloadofdata, the 3 size is sizeof(shitloadofdata) and not a 2 byte.

Polymorphism is a far better option 1 than unions.

Score: 2

The thing I don't like about unions is that 19 they are undiscriminating; they give no 18 info about what the underlying type currently 17 is, and it is very very easy to violate 16 type safety by accessing the wrong side 15 of the union.

Boost::variant solves a lot of these problems. As 14 the documentation points out, union is "nearly 13 useless in an object-oriented environment", while 12 boost::variant gives a very object oriented 11 approach to solving the practical union 10 problems. It's interface is designed to 9 not allow access to the variant unless you 8 are using the proper type, and the "visitor" pattern 7 example they provide gives compile time 6 errors if the union is extended to include 5 a type you didn't expect.

As for if it is 4 useful; I think so. I've used them to simply 3 large interfaces

 class some_xml_class {
 public:
    void set_property(const string&, const string&);
    void set_property(const string&, const vector<string>&);
    void set_property(const string&, const set<string>&);
    void set_property(const string&, int);

    void set_super_property(const string&, const string&);
    void set_super_property(const string&, const vector<string>&);
    void set_super_property(const string&, const set<string>&);
    void set_super_property(const string&, int);

verses

 class some_xml_class {
 public:
    typedef boost::variant<string, vector<string>, set<string>, int> property_type;
    void set_property(const string&, const property_type&);
    void set_super_property(const string&, const property_type&);

(templates could also 2 be useful here, but let's say the impl was 1 long enough I didn't want to inline it)

Score: 0

Keep in mind that the C++11 standard says 7 that usage is undefined behavior, see M.M's answer (which 6 is IMHO the best answer here). Future standards 5 might define it, but still multibyte number 4 can be stored in big or little endian way. So you should not use unions neither type casting for type puning if portability is an issue.

If 3 it is not an issue, let me try to upgrade 2 your code a bit to show how the unions might 1 find their place under the C++ sun.

class Color {
    union {
        uint32_t value;
        struct {
            uint8_t b, g, r; // LSB value expected
        } part;
    } data;
public:
    uint32_t &value;
    uint8_t &r, &g, &b;
    Color()
        : value(data.value)
        , r(data.part.r)
        , g(data.part.g)
        , b(data.part.b)
    { value = 0; }
    Color(Color const& c) : Color() { value = c.value; }
    Color(uint32_t _value) : Color() { value = _value; }
    Color& operator=(Color const& c) { value = c.value;}
    Color& operator=(uint32_t _value) { value = _value;}
};

Explanation

  • struct inside union should have name: ISO C++ prohibits anonymous structs, however g++ would compile them
  • other constructors delegates to default constructor to copy values, not references (C++11)
  • the third constructor is here for comfortable implicit call
  • the construct assumes that multibyte numbers are stored in LSB order

Usage

Color c1 = 0xAABBCC; // implicit call
std::cout << hex << +c1.r << endl; // AA
Color c2 = c1; // copy constructor
c2.g = 127;
std::cout << hex << +c1.g << " " << +c2.g << endl; // BB 7F
c1 = 0xDEADCD; // assignment operator
std::cout << hex << +c1.g << " " << +c2.g << endl; // AD 7F

More Related questions