C
enum stuff q;
enum stuff {a, b=-4, c, d=-2, e, f=-3, g} s;
Declaration which acts as a tentative definition of a signed integer s
with complete type and declaration which acts as a tentative definition of signed integer q
with incomplete type in the scope (which resolves to the complete type in the scope because the type definition is present anywhere in the scope) (like any tentative definition, the identifiers q
and s
can be redeclared with the incomplete or complete version of the same type int
or enum stuff
multiple times but only defined once in the scope i.e. int q = 3; and can only be redefined in a subscope, and only usable after the definition). Also you can only use the complete type of enum stuff
once in the scope because it acts as a type definition.
A compiler enumeration type definition for enum stuff
is also made present at file scope (usable before and below) as well as a forward type declaration (the type enum stuff
can have multiple declarations but only one definition/completion in the scope and can be redefined in a subscope). It also acts as a compiler directive to substitute a
with rvalue 0
, b
with -4
, c
with 5
, d
with -2
, e
with -3
, f
with -1
and g
with -2
in the current scope. The enumeration constants now apply after the definition until the next redefinition in a different enum which cannot be on the same scope level.
typedef enum bool {false, true} bool;
//this is the same as
enum bool {false, true};
typedef enum bool bool;
//or
enum bool {false, true};
typedef unsigned int bool;
//remember though, bool is an alias for _Bool if you include stdbool.h.
//and casting to a bool is the same as the !! operator
The tag namespace shared by enum, struct and union is separate and must be prefixed by the type keyword (enum, struct or union) in C i.e. after enum a {a} b
, enum a c
must be used and not a c
. Because the tag namespace is separate to the identifier namespace, enum a {a} b
is allowed but enum a {a, b} b
is not because the constants are in the same namespace as the variable identifiers, the identifier namespace. typedef enum a {a,b} b
is also not allowed because typedef-names are part of the identifier namespace.
The type of enum bool
and the constants follow the following pattern in C:
+--------------+-----+-----+-----+
| enum bool | a=1 |b='a'| c=3 |
+--------------+-----+-----+-----+
| unsigned int | int | int | int |
+--------------+-----+-----+-----+
+--------------+-----+-----+-----+
| enum bool | a=1 | b=-2| c=3 |
+--------------+-----+-----+-----+
| int | int | int | int |
+--------------+-----+-----+-----+
+--------------+-----+---------------+-----+
| enum bool | a=1 |b=(-)0x80000000| c=2 |
+--------------+-----+---------------+-----+
| unsigned int | int | unsigned int | int |
+--------------+-----+---------------+-----+
+--------------+-----+---------------+-----+
| enum bool | a=1 |b=(-)2147483648| c=2 |
+--------------+-----+---------------+-----+
| unsigned int | int | unsigned int | int |
+--------------+-----+---------------+-----+
+-----------+-----+---------------+------+
| enum bool | a=1 |b=(-)0x80000000| c=-2 |
+-----------+-----+---------------+------+
| long | int | long | int |
+-----------+-----+---------------+------+
+-----------+-----+---------------+------+
| enum bool | a=1 | b=2147483648 | c=-2 |
+-----------+-----+---------------+------+
| long | int | long | int |
+-----------+-----+---------------+------+
+-----------+-----+---------------+------+
| enum bool | a=1 | b=-2147483648 | c=-2 |
+-----------+-----+---------------+------+
| int | int | int | int |
+-----------+-----+---------------+------+
+---------------+-----+---------------+-----+
| enum bool | a=1 | b=99999999999 | c=1 |
+---------------+-----+---------------+-----+
| unsigned long | int | unsigned long | int |
+---------------+-----+---------------+-----+
+-----------+-----+---------------+------+
| enum bool | a=1 | b=99999999999 | c=-1 |
+-----------+-----+---------------+------+
| long | int | long | int |
+-----------+-----+---------------+------+
This compiles fine in C:
#include <stdio.h>
enum c j;
enum c{f, m} p;
typedef int d;
typedef int c;
enum c j;
enum m {n} ;
int main() {
enum c j;
enum d{l};
enum d q;
enum m y;
printf("%llu", j);
}
C++
In C++, enums can have a type
enum Bool: bool {True, False} Bool;
enum Bool: bool {True, False, maybe} Bool; //error
In this situation, the constants and the identifier all have the same type, bool, and an error will occur if a number cannot be represented by that type. Maybe = 2, which isn't a bool. Also, True, False and Bool cannot be lower case otherwise they will clash with language keywords. An enum also cannot have a pointer type.
The rules for enums are different in C++.
#include <iostream>
c j; //not allowed, unknown type name c before enum c{f} p; line
enum c j; //not allowed, forward declaration of enum type not allowed and variable can have an incomplete type but not when it's still a forward declaration in C++ unlike C
enum c{f, m} p;
typedef int d;
typedef int c; // not allowed in C++ as it clashes with enum c, but if just int c were used then the below usages of c j; would have to be enum c j;
[enum] c j;
enum m {n} ;
int main() {
[enum] c j;
enum d{l}; //not allowed in same scope as typedef but allowed here
d q;
m y; //simple type specifier not allowed, need elaborated type specifier enum m to refer to enum m here
p v; // not allowed, need enum p to refer to enum p
std::cout << j;
}
Enums variables in C++ are no longer just unsigned integers etc, they're also of enum type and can only be assigned constants in the enum. This can however be cast away.
#include <stdio.h>
enum a {l} c;
enum d {f} ;
int main() {
c=0; // not allowed;
c=l;
c=(a)1;
c=(enum a)4;
printf("%llu", c); //4
}
Enum classes
enum struct
is identical to enum class
#include <stdio.h>
enum class a {b} c;
int main() {
printf("%llu", a::b<1) ; //not allowed
printf("%llu", (int)a::b<1) ;
printf("%llu", a::b<(a)1) ;
printf("%llu", a::b<(enum a)1);
printf("%llu", a::b<(enum class a)1) ; //not allowed
printf("%llu", b<(enum a)1); //not allowed
}
The scope resolution operator can still be used for non-scoped enums.
#include <stdio.h>
enum a: bool {l, w} ;
int main() {
enum a: bool {w, l} f;
printf("%llu", ::a::w);
}
But because w cannot be defined as something else in the scope, there is no difference between ::w
and ::a::w