Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any guarantees for unions that contain a wrapped type and the type itself?

Can I put a T and a wrapped T in an union and inspect them as I like?

union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};
// for simplicity T = int

Example ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // ?

The C++11 standards only guarantee save inspection of the common initial sequence, but value isn't a struct. I guess the answer is no, since wrapped types aren't even guaranteed to be memory compatible to their unwrapped counterpart and accessing inactive members is only well-defined on common initial sequences.

like image 346
Zeta Avatar asked Jan 02 '18 09:01

Zeta


1 Answers

I believe this is undefined behavior.

[class.mem] gives us:

The common initial sequence of two standard-layout struct types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types and either neither entity is a bit-field or both are bit-fields with the same width. [...]

In a standard-layout union with an active member of struct type T1, it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2; the behavior is as if the corresponding member of T1 were nominated.

If T isn't a standard layout struct type, this is clearly undefined behavior. (Note that int is not a standard layout struct type, as it's not a class type at all).

But even for standard layout struct types, what constitutes a "common initial sequence" is based strictly on non-static data members. That is, T and struct { T val; } do not have a common initial sequence - there are no data members in common at all!

Hence, here:

template <typename T>
union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};


Example<int> ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // (*)

you're accessing an inactive member of the union. That's undefined.

like image 84
Barry Avatar answered Oct 09 '22 02:10

Barry