https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58559
--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> --- I don't think there's a bug here. Your program actually has undefined behaviour due to the (E)-1 conversion. Using -fno-strict-enums only tells GCC not to optimize on the assumption you never do (E)-1 but it doesn't actually make it valid. The underlying type of the enum is implementation-defined, and GCC's choice of unsigned for your type is documented at https://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit-fields-implementation.html The value range of values of the enum are only 0 and 1, so (E)-1 is undefined. [conv.prom] p3 in the standard is clear that the enumeration type promotes to int, because that is the first type in the list that can represent all the values of the type (i.e. 0 and 1). Using -fno-strict-enums doesn't change those promotion rules (doing so would be an ABI change). When you convert it explicitly as (long long)e the warning is correct, because the underlying type is unsigned and so it can't have any values that will produce a negative long long. When you do (e < 0) there is an implicit conversion to int (as per the rules of [conv.prom] p3) and (int)(E)-1 _does_ produce a negative value. So there is no hybrid type, it's just that the promotion rules of C++ do not require promotion to the underlying type, but to the first type that can represent all the valid values. - If you don't use invalid values, there are no surprises. i.e. don't use (E)-1 - If you explicitly convert to the underlying type instead of allowing implicit promotion to int, there are no surprises e.g. this won't abort if (((long long)e < 0) != ((std::underlying_type_t<E>)e < 0)) abort(); - If you give the enumeration a fixed underlying type, there are no surprises e.g. enum E : int { A, B }; // ((long long)e < 0) == (e < 0) and no warnings - If you add a large enumerator so that the range of valid values is increased, there are no surprises: enum E { A, B, _max = -1u }; // (e < 0) and f(e) promote to unsigned now So I think this is only a problem for bad code, and the C++ language provides several ways to avoid the problem. Relying on -fno-strict-enums and using invalid values of the enum type is a bad idea. Arguably, we *could* make -fno-strict-enums act as though all enums have a fixed underlying type, so that all values of the underlying type (in your case that's unsigned) are valid values. That would mean the implicit conversion required by [conv.prom] p3 would convert to the underlying type. But as I said above, that would be an ABI change.