Reputation: 315
I know that we can access anonymous unions without creating it's object(without dot), but could anybody please explain,what is the use of anonymous unions in real world c++ programing?
Upvotes: 4
Views: 766
Reputation: 170064
I actually remembered a use case I came across a while back. You know bit-fields? The standard makes very little guarantees about their layout in memory. If you want to pack binary data into an integer of a specific size, you are usually better off doing bit-wise arithmetic yourself.
However, with unions and the common initial sequence guarantee, you can put all the boilerplate behind member access syntax. So your code will look like it's using a bit-field, but will in fact just be packing bits into a predictable memory location.
Here's a Live Example
#include <cstdint>
#include <type_traits>
#include <climits>
#include <iostream>
template<typename UInt, std::size_t Pos, std::size_t Width>
struct BitField {
static_assert(std::is_integral<UInt>::value && std::is_unsigned<UInt>::value,
"To avoid UB, only unsigned integral type are supported");
static_assert(Width > 0 && Pos < sizeof(UInt) * CHAR_BIT && Width < sizeof(UInt) * CHAR_BIT - Pos,
"Position and/or width cannot be supported");
UInt mem;
BitField& operator=(UInt val) {
if((val & ((UInt(1) << Width) - 1)) == val) {
mem &= ~(((UInt(1) << Width) - 1) << Pos);
mem |= val << Pos;
}
// Should probably handle the error somehow
return *this;
}
operator UInt() {
return (mem >> Pos) & Width;
}
};
struct MyColor {
union {
std::uint32_t raw;
BitField<std::uint32_t, 0, 8> r;
BitField<std::uint32_t, 8, 8> g;
BitField<std::uint32_t, 16, 8> b;
};
MyColor() : raw(0) {}
};
int main() {
MyColor c;
c.r = 0xF;
c.g = 0xA;
c.b = 0xD;
std::cout << std::hex << c.raw;
}
Upvotes: 2
Reputation: 29952
Here's a real-world example:
struct Point3D {
union {
struct {
float x, y, z;
};
struct {
float c[3];
};
};
};
Point3D p;
You can access p
's x/y/z coordinates with p.x
, p.y
, p.z
. This is convenient.
But sometimes you want to access point as a float[3]
array. You can use p.c
for that.
Note: Using this construct is Undefined Behavior by the standard. But, it works on all compilers I've met so far. So, if you want to use such a construct, be aware, that this may broke some day.
Upvotes: 4
Reputation: 5421
I have mostly used unions to store multiple different types of elements in the same contiguous storage without resorting to dynamic polymorphism. Thus, every element of my union is a struct describing the data for the corresponding node type. Using an anonymous union mostly gives a more convenient notation, i.e. instead of object.union_member.struct_member
, I can just write object.struct_member
, since there is no other member of that name anyways.
A recent example where I used them would be a rooted (mostly binary) tree which has different kinds of nodes:
struct multitree_node {
multitree_node_type type;
...
union {
node_type_1 n1;
node_type_2 n2;
...
};
};
Using this type tag type
I am able to determine which element of the union to use. All of the structs node_type_x
have roughly the same size, which is why I used the union in the first place (no unused storage).
With C++17, you would be able to do this using std::variant
, but for now, using anonymous unions are a convenient way of implementing such 'polymorphic' types without virtual
functions.
Upvotes: 6