isn't well defined either
It is one of those borderline edge cases of the language standard.
There are several classes of behavior:
There is well-defined behavior that is specified in the standard. These things MUST be a particular way. Most of the language standard falls in this arena.
There is implementation defined behavior that is outlined by the standard but the compiler or system sets the value. These are usually properties of the underlying machine or compiler such as the number of bits in a data type, the maximum number of characters in a name, the depth of nesting that is legal, and so on.
There is unspecified behavior that is generally outlined by the standard. Usually the standard defines several options and allows the system to do whatever they want inside those bounds. For example, the order of evaluation of function arguments is unspecified. The standard requires the process must happen, but the standard allows broad latitude to how and when it happens.
There is undefined behavior that the standard has no requirements for. The most classic of these is the result of following a null pointer or following a pointer into unknown locations. The behavior is completely undefined by the standard and the system could do anything, from crashing, to warning with error messages, to doing what the programmer thinks might happen.
Individual compilers are allowed to write their own extensions and define their own behavior. They must provide the values for implementation defined behavior, such as stating that the maximum identifier length is x bytes, or the allowable depth of template recursion, and so on.
In this case the standard defines that they must exist, but allows most of the underlying details to be unique to the compiler.
Bit fields have a lot of implementation defined behavior. The underlying type, the number of bits, the packing of the bits, the allocation within a class object, the alignment, the straddling of allocation units, all of these are implementation defined.
Yes, technically you're only allowed to read from the same union member which was previously written. However, (almost) every C/C++ compiler actually recommends using unions in this exact way when you intend to perform a bitwise reinterpretation of a value.
Yes, this is one of those obscure little semi-guarantees in the standard that ALMOST every compiler takes steps to ensure.
The standard offers a guarantee, but the guarantee is based on implementation defined behavior.
Under class.union section: Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members; see 9.2. —end note
With a bit of a cross reference back to 9.2, a bit-field is compatible as long as both the bit-field and the other type are layout-compatible and have the same width.
So even though all the details are implementation defined, as long as the implementation definition for the types are layout-compatible with the same width, it is perfectly legal. These days ever compiler I know of makes certain the bit-field's implementation is compatible with the other integral types.
Back in the bad-old-days (and likely even today for a few obscure or severely-limited chips) there were chipsets that did not have operations for bit shifting. Since bit packing is very important for many systems the operations are usually included, often with a magical piece of hardware called a
barrel shifter because it is so common and important to be fast. But some small number of systems did not include the functionality, so packing and unpacking bits was/is much more difficult. In these systems a c++ implementation may decide the effort is not worth the reward, but I'm not aware of any of these systems in use today.