Improve this page Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using local clone. Page wiki View or edit the community-maintained wiki page associated with this page.

Enums

EnumDeclaration:
    enum EnumTag EnumBody
    enum EnumBody
    enum EnumTag : EnumBaseType EnumBody
    enum : EnumBaseType EnumBody

EnumTag:
    Identifier

EnumBaseType:
    Type

EnumBody:
    EmptyEnumBody
    EnumMembersBody

EmptyEnumBody:
    ;

EnumMembersBody:
    { EnumMembers }

EnumMembers:
    EnumMember
    EnumMember ,
    EnumMember , EnumMembers

EnumMember:
    Identifier
    Identifier = AssignExpression
    Type Identifier = AssignExpression

Enum declarations are used to define a group of constants. They come in these forms:

  1. Named enums, which have an EnumTag.
  2. Anonymous enums, which do not have an EnumTag.
  3. Manifest constants.

Named Enums

Named enums are used to declare related constants and group them by giving them a unique type. The EnumMembers are declared in the scope of the enum EnumTag. The enum EnumTag declares a new type, and all the EnumMembers have that type.

This defines a new type X which has values X.A=0, X.B=1, X.C=2:

enum X { A, B, C }  // named enum

If the EnumBaseType is not explicitly set, and the first EnumMember has an AssignExpression, it is set to the type of that AssignExpression. Otherwise, it defaults to type int.

Named enum members may not have individual Types.

A named enum member can be implicitly cast to its EnumBaseType, but EnumBaseType types cannot be implicitly cast to an enum type.

The value of an EnumMember is given by its AssignExpression. If there is no AssignExpression and it is the first EnumMember, its value is EnumBaseType.init.

If there is no AssignExpression and it is not the first EnumMember, it is given the value of the previous EnumMember+1. If the value of the previous EnumMember is EnumBaseType.max, it is an error. If the value of the previous EnumMember+1 is the same as the value of the previous EnumMember, it is an error. (This can happen with floating point types.)

All EnumMembers are in scope for the AssignExpressions.

enum A = 3;
enum B
{
    A = A // error, circular reference
}
enum C
{
    A = B,  // A = 4
    B = D,  // B = 4
    C = 3,  // C = 3
    D       // D = 4
}
enum E : C
{
    E1 = C.D,
    E2      // error, C.D is C.max
}

An EmptyEnumBody signifies an opaque enum - the enum members are unknown.

Enum Default Initializer

The .init property of an enum type is the value of the first member of that enum. This is also the default initializer for the enum type.

enum X { A=3, B, C }
X x;      // x is initialized to 3

Enum Properties

Enum properties only exist for named enums.

.initFirst enum member value
.minSmallest value of enum
.maxLargest value of enum
.sizeofSize of storage for an enumerated value

For example:

enum X { A=3, B, C }
X.min    // is X.A
X.max    // is X.C
X.sizeof // is same as int.sizeof

The EnumBaseType of named enums must support comparison in order to compute the .max and .min properties.

Anonymous Enums

If the enum Identifier is not present, then the enum is an anonymous enum, and the EnumMembers are declared in the scope the EnumDeclaration appears in. No new type is created.

The EnumMembers can have different types. Those types are given by the first of:

  1. The Type, if present.
  2. The EnumBaseType, if present.
  3. The type of the AssignExpression, if present.
  4. The type of the previous EnumMember, if present.
  5. int
enum { A, B, C }  // anonymous enum

Defines the constants A=0, B=1, C=2, all of type int.

Enums must have at least one member.

The value of an EnumMember is given by its AssignExpression. If there is no AssignExpression and it is the first EnumMember, its value is the .init property of the EnumMember's type.

If there is no AssignExpression and it is not the first EnumMember, it is given the value of the previous EnumMember+1. If the value of the previous EnumMember is the .max property if the previous EnumMember's type, it is an error. If the value of the previous EnumMember+1 is the same as the value of the previous EnumMember, it is an error. (This can happen with floating point types.)

All EnumMembers are in scope for the AssignExpressions.

enum { A, B = 5+7, C, D = 8+C, E }

Sets A=0, B=12, C=13, D=21, and E=22, all of type int.

enum : long { A = 3, B }

Sets A=3, B=4 all of type long.

enum : string
{
    A = "hello",
    B = "betty",
    C     // error, cannot add 1 to "betty"
}
enum
{
    A = 1.2f,  // A is 1.2f of type float
    B,         // B is 2.2f of type float
    int C = 3, // C is 3 of type int
    D          // D is 4 of type int
}

Manifest Constants

If there is only one member of an anonymous enum, the { } can be omitted:

enum i = 4;      // i is 4 of type int
enum long l = 3; // l is 3 of type long

Such declarations are not lvalues, meaning their address cannot be taken. They exist only in the memory of the compiler.

enum size = __traits(classInstanceSize, Foo);  // evaluated at compile-time

Using manifest constants is an idiomatic D method to force compile-time evaluation of an expression.

Forums | Comments | Search | Downloads | Home