Search code examples
c++language-lawyerundefined-behaviorvolatileunions

Union with volatile and non-volatile standard layout types


It is legal to use active and non-active members of a union if they are standard layout types e.g. like primitive types as int.

On the other hand it is UB to const_cast-away the volatile of a simple variable and use that variable.

Is it legal (no UB) to use both members of this union?

union VU {
    int nv;
    volatile int v;
};

More formal this should be

union VU {
    struct {
        int v;
    } nv;
    struct {
        volatile int v;
    } v;
};

Solution

  • If by "use both members of this union" you mean attempting to exploit the common initial sequence rules to access a volatile object through a non-volatile glvalue:

    union VU {
        struct {
            int v;
        } nv;
        struct {
            volatile int v;
        } v;
    };
    VU x;
    x.v.v = 42;
    std::cout << x.nv.v;
    

    the answer is no, it's not legal. [class.mem.general]/26 is actually very clear about this:

    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. [Example 5: ... ] [Note 10: Reading a volatile object through a glvalue of non-volatile type has undefined behavior ([dcl.type.cv]). — end note]

    In the above example, the behaviour of reading x.nv.v is as if the corresponding member of the actually active member were nominated, i.e., it reads the member x.v.v of the active member x.v. Since this is a read of the volatile object x.v.v. through a non-volatile glvalue, the behavior is undefined.

    On the other hand if you were to do it the other way around (make x.nv.v active, then read it through x.v.v) then it would be legal; it wouldn't be any different from reading through const_cast<volatile int&>(x.nv.v).