Around the time C++17 was being standardized I saw magical terms like “discriminated union”, “type-safe union” or “sum type” floating around. Later it appeared to mean the same type: “variant”.
It is undefined behaviour to read a union member with a different type from the one with which it was written. Such punning is invisible, or at least harder to spot than using a named cast. Type punning using a union is a source of errors.
There’s also additional issue with unions: they’re very simple and crude. You don’t have a way to know what’s the currently used type and what’s more they won’t call destructors of the underlying types. Here’s an example from cppreference/union that clearly illustrate how hard it might be:
As you see, the S union needs a lot of maintenance from your side. You have to know which type is active and adequately call destructors/constructors before switching to a new variant.