C++26에서 제거되는 두 가지 언어 기능—열거형의 산술 변환과 C 스타일 배열 비교—를 배경, 예시, 컴파일러 지원 현황과 함께 정리합니다.
아마 모두들 C++가 끝없이 커지는 언어라는 말을 들어보셨을 겁니다. 저도 여러 번 그렇게 썼고요. 실제로 각 표준은 많은 이들이 고대하던 기능들을 한 묶음으로 가져옵니다. 동시에 바이너리 호환성 고려로 인해 오래된 기능들 중 실제로 제거되는 것은 아주 적습니다. 이는 몇 가지 함의를 가집니다.
이는 사실이지만, 매 표준마다 일부 기능이 제거된다는 점도 잊지 말아야 합니다. 이 글에서는 C++26에서 제거되는 언어 기능들을 살펴보고, 후속 글에서는 제거된 라이브러리 기능들을 보겠습니다.
이 지점에서 한 가지 짚고 넘어갈 것은, 언어에서 어떤 것을 제거하는 작업은 보통 두 단계로 이뤄진다는 점입니다. 먼저 기능이 사용 중단(deprecated) 표시가 되어, 해당 기능을 사용하면 컴파일러 경고가 뜹니다. 다음 단계(어떤 경우엔 영원히 오지 않기도 함)에서 컴파일러 지원이 최종적으로 제거됩니다.
P2864R2는 산술 변환 과정에서 열거형 값을 암시적으로 변환하는 기능을 제거합니다.
문서의 7, 8, 9장을 보면, 과거의 것을 없애 언어를 더 간결하게 만들기 위해 합의를 이끌어내는 일이 얼마나 어려운지 알 수 있습니다. 때로는 사용 중단과 좋지 않은 경험을 겪고 나서도, 사용 중단된 기능을 되살리는 것까지 고려해야 하기도 합니다.
이번 경우엔 마침내 기능이 제거됩니다. C++26부터는 한 피연산자가 열거형 타입이고 다른 피연산자가 다른 열거형 타입이거나 부동소수점 타입인 식은 ill-formed가 됩니다.
아래 줄들을 제가 직접 쓸 일은 거의 상상하기 어렵지만, 어떤 상황에서는 누군가 그렇게 하고 싶어 했던 모양입니다.
1
2
3
4
5
6
int main() {
enum E1 { e };
enum E2 { f };
bool b = e <= 3.7; // 더 이상 언어 차원에서 지원되지 않음
int k = f - e; // 더 이상 언어 차원에서 지원되지 않음
}
그래도 여전히 이런 식을 컴파일하게 만들고 싶다면(아마 그러면 안 됩니다), 단항 operator+로 enum 값을 정수로 승격시키는 꼼수를 쓸 수 있습니다…
1
2
3
4
5
6
int main() {
enum E1 { e };
enum E2 { f };
bool b = +e <= 3.7; // 그다지 예쁘지 않은 임시방편
int k = +f - e; // 제발 그러지 마세요
}
Clang 18과 GCC 14는 이 제거를 구현했습니다.
P2865R6는 C++20에서 우주선 연산자(<=>)의 도입과 함께 이미 사용 중단된 C 스타일 배열 간의 비교를 C++에서 제거합니다.
우주선 연산자(<=>)를 사용하면 배열과 그 내용(을 포함한 여러 대상을) 올바르게 비교할 수 있지만, 기존 비교 연산자들로는 배열이 포인터로 붕괴되는 특성 때문에 실제로는 메모리 주소가 비교되었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
int main() {
int a[5] = {0, 1, 2, 3, 4};
int b[5] = {0, 1, 2, 3, 4};
if ( a == b ) { // C++20부터 사용 중단, C++26부터 ill-formed
std::cout << "a와 b는 같은 주소를 가집니다\n";
} else {
std::cout << "a와 b는 값은 같을지 몰라도, 내용은 서로 다릅니다\n";
}
}
/*
a와 b는 값은 같을지 몰라도, 내용은 서로 다릅니다
*/
이것만으로도 충분히 나쁜데, 대소 비교의 경우 결과가 종종 미정의이기도 했습니다. 설령 어떤 구현에서 정의되어 있다 해도, 그것이 코드 작성자가 의도한 바와 일치할 가능성은 높지 않습니다.
이러한 비교는 C++26부터 ill-formed지만, 두 피연산자 중 하나가 포인터라면 배열에서 포인터로의 변환은 여전히 가능합니다. 즉, 다음 코드는 그다지 좋진 않지만 유효합니다.
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
int main() {
int a[5];
int* b = a;
if ( a == b ) {
std::cout << "a와 b는 같은 주소를 가집니다\n";
} else {
std::cout << "a와 b는 값은 같을지 몰라도, 내용은 서로 다릅니다\n";
}
}
아울러 이는 배열을 nullptr과 직접 비교할 수 있음을 의미하기도 합니다.
C++는 성장하고 있지만, 거의 모든 새 표준마다 제거되는 기능들도 몇 가지 있습니다. C++26에서는 두 가지 언어 기능이 제거됩니다. 하나는 열거형에서의 산술 변환이고, 다른 하나는 C 스타일 배열의 비교입니다. 둘 다 언어를 단순화할 뿐만 아니라, 해당 코드베이스를 더 깔끔하게 만드는 데도 도움이 된다고 생각합니다.
이 글이 마음에 드셨다면