Enum constants behaving differently in C and C++
Solution 1:
In C, an enum
constant is of type int
. In C++, it's of the enumerated type.
enum en_e{
en_e_foo,
en_e_bar=UINT64_MAX,
};
In C, this is a constraint violation, requiring a diagnostic (if UINT64_MAX
exceeds INT_MAX
, which it very probably does). A C compiler may reject the program altogether, or it may print a warning and then generate an executable whose behavior is undefined. (It's not 100% clear that a program that violates a constraint necessarily has undefined behavior, but in this case the standard doesn't say what the behavior is, so that's still undefined behavior.)
gcc 6.2 doesn't warn about this. clang does. This is a bug in gcc; it incorrectly inhibits some diagnostic messages when macros from standard headers are used. Thanks to Grzegorz Szpetkowski for locating the bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71613
In C++, each enumeration type has an underlying type, which is some integer type (not necessarily int
). This underlying type must be able to represent all the constant values. So in this case, both en_e_foo
and en_e_bar
are of type en_e
, which must be at least 64 bits wide, even if int
is narrower.
Solution 2:
That code just isn't valid C in the first place.
Section 6.7.2.2 in both C99 and C11 says that:
Constraints:
The expression that defines the value of an enumeration constant shall be an integer constant expression that has a value representable as an
int
.
A compiler diagnostic is mandatory because it is a constraint violation, see 5.1.1.3:
A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined.
Solution 3:
In C, while a enum
is considered to be a separate type, enumerators itself always have type int
.
C11 - 6.7.2.2 Enumeration specifiers
3 The identifiers in an enumerator list are declared as constants that have type int...
Thus, behaviour you see is a compiler extension.
I'd say it makes sense to only expand size of one of the enumerators if its value is too large.
On the other hand, in C++ all enumerators have the type of the enum
they're declared in.
Because of that, size of every enumerator must be same. So, size of entire enum
is expanded to store the largest enumerator.
Solution 4:
As others pointed, the code is ill-formed (in C), because of constraint violation.
There is GCC bug #71613 (reported June 2016), which states that some useful warnings are silenced with macros.
Useful warnings seem to be silenced when macros from system headers are used. For example, in the example below a warning would be useful for both enums but only one warning is shown. The same can probably happen for other warnings.
The current workaround may be to prepend the macro with unary +
operator:
enum en_e {
en_e_foo,
en_e_bar = +UINT64_MAX,
};
which yields compilation error on my machine with GCC 4.9.2:
$ gcc -std=c11 -pedantic-errors -Wall main.c
main.c: In function ‘main’:
main.c:9:20: error: ISO C restricts enumerator values to range of ‘int’ [-Wpedantic]
en_e_bar = +UINT64_MAX