Inspect inactive member of a union with common initial sequence in constexpr expression

Viewed 175

C++ Standard in point 12.3.1 says:

If a standard-layout union contains several standard-layout structs that share a common initial sequence, and if a non-static data member of an object of this standard-layout union type is active and is one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of the standard-layout struct members;

However following code doesn't compile on any major compiler (https://godbolt.org/z/3jM1co):

struct type {
    union {
        int a;
        int b;
    };

    constexpr type(int n) : a(n) {}
};

constexpr int fun(int n) {
    type t(n);

    return t.b;
}

constexpr int res = fun(5);

Why code doesn't compile (I belive that all compilers aren't wrong here)? This code should meet the conditions for accessing technically inactive member of a union (standard layout, common initial sequence). When constexpr keyword is removed this code compiles without a problem.

1 Answers

The text that you quoted is correct, but there are additional constraints on accessing an inactive member of a union in a constexpr context. In particular, you are violating this rule:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;


Note that you can change the active member of a union inside a constexpr context, so you can do this:

constexpr int fun(int n) 
{
    type t(n);
    t.b = t.a;    // t.b is now the active member
    return t.b;   // ok, reading from active member is fine
}

I believe this is allowed only from c++20: demo.

The relevant rule is this:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

an invocation of an implicitly-defined copy/move constructor or copy/move assignment operator for a union whose active member (if any) is mutable, unless the lifetime of the union object began within the evaluation of E;

(emphasis is mine). Since the lifetime of t begins inside the evaluation of fun, this is allowed.

Related