#14. [모던 C++] 개선된 열거형(C++11, C++17, C++20)
- [MEC++#10] 범위 없는 열거형보다 범위 있는 열거형을 선호하라.(열거형의 암시적 형변환, 전방 선언, 기반 타입 참고)
- (C++11~) 범위 있는 열거형이 추가되어 이름 충돌 회피가 쉬워졌고, 암시적 형변환을 차단하며, 전방 선언도 지원합니다.
- (C++11~) 열거형의 기반 타입을 지정할 수 있습니다.
- (C++17~) 열거형의 중괄호 직접 초기화를 허용하여 암시적 형변환을 차단하는 사용자 정의 열거형의 사용이 좀더 쉬워졌습니다.
- (C++20~) using enum이 추가되어 범위 있는 열거형의 이름 없이 열거자를 유효 범위내에서 사용할 수 있습니다.
개요
기존에는 열거형의 이름이 한정되지 않아 이름 충돌의 우려가 있어 클래스 내에 정의하는 방식을 사용했었는데요(열거형 참고),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 기존
enum Week {
Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
Week week{Sunday}; // (△) 비권장. 이름 충돌이 쉬움
// 혹은
class Week {
public:
// 클래스내에 정의. 사용시 클래스명을 기재해야 함
enum Val {
Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
};
Week::Val val{Week::Sunday}; // 범위 확인 연산자와 클래스명 사용
C++11 부터는 범위 있는 열거형을 지원하여 이름 충돌 회피가 훨씬 쉬워졌습니다.
1
2
3
4
5
6
// 범위 있는 열거형
enum class Week_11 {
Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
Week_11 week{Week_11::Sunday}; // 열거형 이름을 같이 사용하여 이름 충돌 회피
열거형의 암시적 형변환
기존 열거형은 암시적으로 형변환이 되지만, 범위 있는 열거형은 형변환되지 않습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Week {
Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
Week week = Sunday;
int val = week; // int형으로 형변환 됩니다.
enum class Week_11 {
Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
Week_11 week_11 = Week_11::Sunday;
// int val_11 = week_11; // (X) 컴파일 오류. 형변환되지 않습니다.
전방 선언
범위 있는 열거형은 전방 선언을 지원합니다. 지난날 열거형이 정의된 헤더 파일이 수정될 때마다 엄청나게 많은 파일들이 쓸데없이 재빌드 되던 걸 생각하면, 참 사소하지만 감사한 기능입니다.
1
2
enum MyEnum; // (X) 컴파일 오류.
enum class MyEnum_11; // (O)
기반 타입
기존에는 열거형의 크기를 지정하기 위하여 열거자에 강제적으로 더미(Dummy) 값을 입력했는데요(열거형의 크기 참고),
C++11 부터는 열거형의 기반 타입을 지정할 수 있습니다. 기본적으로는 int
를 사용하며, 다음과 같이 명시적으로 변경할 수 있습니다.
1
2
enum MyEnum1_11 : int {a, b, c}; // int 형을 기반 타입으로 사용합니다.
enum class MyEnum2_11 : char {i, j, k}; // char 형을 기반 타입으로 사용합니다.
열거형 초기화와 암시적 형변환 차단
열거자에 열거형의 기반 타입만 지정하고 열거자를 생략할 수 있습니다.
1
enum MyInt_11 : int {}; // 열거자가 빠졌습니다!!
보통 나열된 열거자를 사용하기 위해 열거형을 정의하는데, 이를 생략하다니 좀 의아한데요, 재밌게도 다음과 같이 값을 대입할 수 있습니다. 반드시 같은 타입의 열거형만 대입할 수 있습니다. 단, 중괄호 직접 초기화는 지원하지 않습니다.
(C++17~) 열거형의 중괄호 직접 초기화를 허용하여 암시적 형변환을 차단하는 사용자 정의 열거형의 사용이 좀더 쉬워졌습니다.
1
2
3
4
5
6
7
8
9
10
11
enum MyInt_11 : int {};
// 특정 값을 대입합니다. 반드시 MyInt 타입만 대입받을 수 있습니다.
MyInt_11 val1{MyInt_11(10)};
MyInt_11 val2 = MyInt_11(10); // MyInt_11 val1{MyInt_11(10)}; 와 동일
MyInt_11 val3{val1};
// MyInt_11 val4 = 10; // (X) 컴파일 오류. 정수는 대입 받을 수 없습니다.
// MyInt_11 val5(10); // (X) 컴파일 오류. 정수는 대입 받을 수 없습니다.
// MyInt_11 val6{10}; // (X) 컴파일 오류. 중괄호 직접 초기화는 지원하지 않습니다.
// MyInt_11 val7 = {10}; // (X) 컴파일 오류. 암시적으로 중괄호 직접 초기화를 사용하므로 지원하지 않습니다.
이 기능을 이용하면, 암시적 형변환을 차단하는 정수 타입을 만들 수 있습니다. 다음 예에서 Func()
은 암시적으로 int
로 변환될 수 있는 모든 타입이 전달될 수 있지만, Func_11()
은 오로지 MyInt_11
만 전달 받을 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
enum MyInt_11 : int {};
void Func(int val) {}
void Func_11(MyInt_11 val) {}
Func(10);
Func('c'); // (△) 비권장. 암시적 형변환 됩니다.
// Func_11(10); // (X) 컴파일 오류. 암시적 형변환을 차단합니다.
// Func_11('c'); // (X) 컴파일 오류. 암시적 형변환을 차단합니다.
Func_11(MyInt_11(10)); // MyInt_11 타입만 가능합니다.
(C++17~) 열거형의 중괄호 직접 초기화 허용
C++17 부터는 열거형의 중괄호 직접 초기화를 허용하여 암시적 형변환을 차단하는 사용자 정의 열거형의 사용이 좀더 쉬워졌습니다.
1
2
3
4
5
6
7
8
9
10
enum MyInt_11 : int {};
MyInt_11 val1{MyInt_11(10)};
MyInt_11 val2 = MyInt_11(10); // MyInt_11 val1{MyInt_11(10)}; 와 동일
MyInt_11 val3{val1};
// MyInt_11 val4 = 10; // (X) 컴파일 오류. 정수는 대입 받을 수 없습니다.
// MyInt_11 val5(10); // (X) 컴파일 오류. 정수는 대입 받을 수 없습니다.
MyInt_11 val6_17{10}; // (O) C++17~ 중괄호 직접 초기화를 지원합니다.
// MyInt_11 val7_17 = {10}; // (X) 컴파일 오류. 중괄호 직접 초기화는 허용하지만, {10}은 int로 추론되어 사용할 수 없습니다.
(C++20~) using enum
범위 있는 열거형 덕에 이름 충돌은 회피되어 좋지만, 매번 열거형의 이름을 함께 명시하다 보니 코드가 지저분해질 수 있는데요,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum class Week_11 {
Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
Week_11 week{Week_11::Sunday};
bool isFreeDay{false};
switch(week) {
case Week_11::Sunday: isFreeDay = true; break;
case Week_11::Monday: break;
case Week_11::Tuesday: break;
case Week_11::Wednesday: break;
case Week_11::Thursday: break;
case Week_11::Friday: break;
case Week_11::Saturday: break;
}
C++20 부터는 using enum이 추가되어 범위 있는 열거형의 이름 없이 열거자를 유효 범위내에서 사용할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum class Week_11 {
Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
Week_11 week{Week_11::Sunday};
bool isFreeDay{false};
switch(week) {
using enum Week_11; // (C++20~) 유효 범위 내에서 Week_11의 열거자를 사용할 수 있습니다.
case Sunday: isFreeDay = true; break;
case Monday: break;
case Tuesday: break;
case Wednesday: break;
case Thursday: break;
case Friday: break;
case Saturday: break;
}
댓글남기기