열거형
열거형 (Enums)
D에서 열거형은 연관된 상수들을 묶어 고유한 타입을 부여하는 선언이에요. 명명된 열거형(named enum)은 새 타입을 만들고, 익명 열거형은 상수만 선언하며, 단일 멤버 문법으로 매니페스트 상수(manifest constant)를 만들 수도 있어요.
출처: Enums
본문
문법
EnumDeclaration:
enum Identifier EnumBody
enum Identifier : EnumBaseType EnumBody
AnonymousEnumDeclaration
EnumBaseType:
Type
EnumBody:
{ EnumMembers }
;
EnumMembers:
EnumMember
EnumMember ,
EnumMember , EnumMembers
EnumMember:
EnumMemberAttributesopt Identifier
EnumMemberAttributesopt Identifier = AssignExpression
EnumMemberAttributes:
EnumMemberAttribute
EnumMemberAttribute EnumMemberAttributes
EnumMemberAttribute:
DeprecatedAttribute
UserDefinedAttribute
@ disable
AnonymousEnumDeclaration:
enum : EnumBaseType { EnumMembers }
enum { AnonymousEnumMembers }
AnonymousEnumMembers:
AnonymousEnumMember
AnonymousEnumMember ,
AnonymousEnumMember , AnonymousEnumMembers
AnonymousEnumMember:
EnumMember
EnumMemberAttributesopt Type Identifier = AssignExpression
열거형 선언은 상수들의 그룹을 정의할 때 써요.
Named Enums
명명된 열거형은 서로 연관된 상수들을 선언하고, 그 상수들에 고유한 타입을 부여해 하나로 묶는 데 사용해요. EnumMembers는 명명된 열거형의 스코프 안에서 선언돼요. 명명된 열거형은 새 타입을 선언하며, 모든 EnumMember는 그 타입을 가져요.
아래는 값이 X.A = 0, X.B = 1, X.C = 2인 새 타입 X를 정의하는 예시예요.
enum X { A, B, C } // named enum
변수는 명명된 열거형 타입이 될 수 있어요. 이때 기본 초기화 값(default initializer)은 그 열거형 타입에서 처음 정의된 멤버예요.
enum X { A = 3, B, C }
X x;
assert(x == X.A);
x |= X.B;
assert(x & X.A);
명명된 열거형에 대한 이항 연산의 결과 타입은 여기에 정의돼 있어요.
EnumBaseType이 명시적으로 설정되지 않고 첫 번째 EnumMember에 AssignExpression이 있다면, EnumBaseType은 그 AssignExpression의 타입으로 정해져요. 그 외의 경우에는 타입 int로 기본 설정돼요.
명명된 열거형 멤버는 자신의 EnumBaseType으로 암시적으로 캐스팅될 수 있어요.
EnumBaseType 인스턴스는 명명된 열거형 타입으로 암시적으로 캐스팅될 수 없어요.
enum Foo { E }
Foo f;
int i;
i = f; // OK
f = i; // error
f = cast(Foo)i; // OK
f = 0; // error
f = Foo.E; // OK
명명된 열거형 멤버는 자신의 Type을 선언할 수 없어요.
참고: 명명된 열거형에 대한 final switch도 참고하세요.
Enum Member Values
EnumMember의 값은 AssignExpression이 있으면 그것으로 주어져요. AssignExpression이 없고 첫 번째 EnumMember라면 그 값은 0에서 EnumBaseType으로 변환된 값이에요. AssignExpression이 없고 첫 번째 멤버가 아니라면 이전 EnumMember의 값 + 1을 가져요.
이전 EnumMember의 값이 EnumBaseType.max라면 오류예요. 이는 값 오버플로를 막기 위한 것이에요. 이전 멤버를 컴파일 타임에 EnumBaseType.max와 비교할 수 없다면 오류예요.
기본 타입이 컴파일 타임에 평가 가능한 + 1 연산을 정의하지 않으면 오류예요.
이전 EnumMember의 값 + 1이 이전 EnumMember의 값과 같다면 오류예요. (부동소수점 타입에서 이런 일이 발생할 수 있어요.)
enum E : char
{
a,
b = char.max,
c // overflow
}
static assert(E.a == 0);
모든 EnumMember는 AssignExpression의 스코프 안에 있어요.
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
}
빈 열거형 본문은 불투명 열거형(opaque enum)을 의미해요 — enum 멤버들이 알려지지 않아요.
enum X; // opaque enum
writeln(X.init); // error: enum X is opaque and has no default initializer
Enum Properties
열거형 속성은 명명된 열거형에만 존재해요.
명명된 열거형 속성
| .init | First enum member value |
|---|---|
| .min | Smallest enum member value |
| .max | Largest enum member value |
| .sizeof | Size of storage for an enumerated value |
예를 들어:
enum X { A = 3, B = 1, C = 4, D, E = 2 }
X.init // is X.A
X.min // is X.B
X.max // is X.D
X.sizeof // is same as int.sizeof
명명된 열거형의 EnumBaseType은 .max와 .min 속성을 계산하려면 비교를 지원해야 해요.
Enum Copying and Assignment
명명된 열거형 타입은 그 EnumBaseType에 정의돼 있더라도 복사 생성자, postblit, identity assignment 오버로드를 절대 갖지 않아요.
기본 타입이 복사 생성자를 가진 struct인 명명된 열거형 값을 복사할 때 복사 생성자는 호출되지 않아요.
struct S
{
this(ref S rhs) { assert(0); }
}
enum E : S { A = S.init }
void main()
{
E e1;
E e2 = e1; // ok - copy constructor not called
}
기본 타입이 postblit을 가진 struct인 명명된 열거형 값을 복사할 때 postblit은 호출되지 않아요.
struct S
{
this(this) { assert(0); }
}
enum E : S { A = S.init }
void main()
{
E e1;
E e2 = e1; // ok - postblit not called
}
같은 타입의 다른 객체에 명명된 열거형 값을 할당할 때, 값들의 기본 타입이 identity assignment 오버로드를 가진 struct라면 identity assignment 오버로드는 호출되지 않아요.
struct S
{
void opAssign(S rhs) { assert(0); }
}
enum E : S { A = S.init }
void main()
{
E e1, e2;
e2 = e1; // ok - opAssign not called
}
Anonymous Enums
enum Identifier가 없으면 그 열거형은 익명 열거형(anonymous enum)이고, EnumMembers는 EnumDeclaration이 나타나는 스코프에 선언돼요. 새 타입은 만들어지지 않아요.
EnumMember들은 서로 다른 타입을 가질 수 있어요. 그 타입들은 다음 중 처음으로 맞는 것으로 주어져요:
- Type (있을 때). Type은 EnumBaseType이 있을 때는 허용되지 않아요.
- EnumBaseType (있을 때).
- AssignExpression의 타입 (있을 때).
- 이전 EnumMember의 타입 (있을 때).
- int
enum { A, B, C } // anonymous enum
위는 A = 0, B = 1, C = 2 상수를 정의하며 모두 타입이 int예요.
열거형은 최소한 멤버 하나가 있어야 해요.
EnumMember의 값은 AssignExpression이 있으면 그것으로 주어져요. AssignExpression이 없고 첫 번째 EnumMember라면 그 값은 EnumMember 타입의 .init 속성이에요. AssignExpression이 없고 첫 번째 멤버가 아니라면 이전 EnumMember의 값 + 1을 가져요.
이전 EnumMember의 값이 이전 EnumMember 타입의 .max 속성이라면 오류예요. 이는 값 오버플로를 막기 위한 것이에요. 이전 멤버를 컴파일 타임에 .max 속성과 비교할 수 없다면 오류예요.
이전 멤버의 타입이 컴파일 타임에 평가 가능한 +1 연산을 정의하지 않으면 오류예요.
이전 EnumMember + 1의 값이 이전 EnumMember의 값과 같다면 오류예요. (부동소수점 타입에서 이런 일이 발생할 수 있어요.)
모든 EnumMember는 AssignExpression의 스코프 안에 있어요.
enum { A, B = 5 + 7, C, D = 8 + C, E }
위는 A = 0, B = 12, C = 13, D = 21, E = 22를 설정하며 모두 타입이 int예요.
enum : long { A = 3, B }
위는 A = 3, B = 4를 설정하며 둘 다 타입이 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
}
Single Member Syntax
익명 열거형의 멤버가 하나뿐이라면 { }를 생략할 수 있어요. 문법적으로 이것은 VarDeclarations의 한 형태예요.
enum i = 4; // i is 4 of type int
enum long l = 3; // l is 3 of type long
이것을 매니페스트 상수(manifest constant)라고도 불러요.
Manifest Constants
열거형 멤버는 매니페스트 상수이며, 컴파일 타임에만 존재해요.
매니페스트 상수는 lvalue가 아니므로 그 주소를 취할 수 없어요. 그것들은 컴파일러의 메모리에만 존재해요.
enum size = __traits(classInstanceSize, Foo); // evaluated at compile-time
매니페스트 상수의 초기화 값은 컴파일 타임 함수 평가(compile-time function evaluation)로 평가돼요.
template Foo(T)
{
// Not bad, but the 'size' variable will be located in the executable.
const size_t size = T.sizeof; // evaluated at compile-time
// ... use of 'size' at compile-time ...
}
template Bar(T)
{
// Better, the manifest constant has no runtime location in the executable.
enum size_t size = T.sizeof; // evaluated at compile-time
// ... use of 'size' at compile time ...
// Taking the address of Foo!T.size also causes it to go into the exe file.
auto p = &Foo!T.size;
}
더 알아보기
- 열거형 타입에 대한 이항 연산의 결과 타입 규칙은 타입(Type) 문서에서 확인할 수 있어요.
- 매니페스트 상수와 컴파일 타임 함수 평가는 CTFE 관련 문서를 참고하세요.
- 명명된 열거형에 대한 final switch 문장을 참고하세요.
- dlang.org의 원문에서 최신 내용을 확인할 수 있어요.