이 코드를 최적화를 키고 컴파일 하면 어떤 어셈블리가 나올까? (https://godbolt.org/z/93qPYjabc)
x의 값이 boring::a건 boring::b건 두 경우 모두 0을 리턴하니까 다음 코드랑 컴파일 결과가 같을거라고 생각할 수 있다
하지만 실제 결과를 보면...
?????? 분명 x의 값은 boring::a나 boring::b 둘중 하나일텐데 컴파일러가 default 케이스를 지워버리지 못하고 있다
gcc 컴파일러가 그냥 멍청한걸까?
clang 컴파일러도 마찬가지의 결과를 내놓는다
왜 그런지 결론부터 말하자면 c++ 표준에 의하면 default케이스도 충분히 실행될 수 있는 코드이기 때문이다
실제로 위의 코드에 UB는 없으며 42라는 값을 잘 출력한다 (https://godbolt.org/z/aMEnM9945)
여기서 boring은 fixed underlying type을 가지는 경우에 속하며 따라서 boring의 값으로 가능한 것들은 boring::a, boring::b와 같은 enumerator들이 아니라 underlying type(int)의 모든 value들이다
즉, 여기에는 42도 가능한 값으로 속하게 된다
덧붙이자면, fixed underlying type을 가지지 않은 enum의 경우에는(주로 unscoped enum) 두번째 줄에 나와있듯이 모든 enumerator들을 저장할 수 있는 가장 작은 크기의 가상의 integer type의 모든 value들이 enum의 값으로 가능하다
예를 들어 boring을 unscoped enum으로 선언했다면 boring의 값으로 가능한 것들은 boring::a, boring::b를 저장하기 위해 필요한 최소한의 크기인 1비트짜리 정수형 타입의 값들이다
1비트짜리 정수형 타입의 값으로 가능한건 단 두가지니까 이 경우에는 운이 좋게도 boring의 값으로 가능한게 boring::a, boring::b 둘뿐으로 딱 맞는 상태라 처음에 원했던대로 최적화가 이루어질 수 있겠지만 godbolt에서 확인해보니 컴파일러들이 실제로 이 최적화를 시행하진 않는다
10번 항목에 의해 main 함수의 static_cast<boring>(42)가 42라는 값을 가진 boring을 만들고
9번 항목에 의해 spooky 함수의 static_cast<int>(x)가 boring을 다시 42라는 값으로 변환한다 (https://timsong-cpp.github.io/cppwp/n4868/expr.static.cast#9)
따라서 해당 코드는 UB 없이 42를 출력하게 되며, 컴파일러는 default 케이스를 최적화해서 없애버릴 수 없다
위에서 말했듯이 boring을 unscoped enum으로 선언해서 코드 최적화를 이끌어내는건 안되지만, underlying type을 bool로 explicit하게 줘버리면 원하던 최적화를 볼 수 있다 (https://godbolt.org/z/jsfKn7TTo)
혹시 오류가 있으면 알려주면 수정함
scoped enum underlying type 관련해 첨언하자면, sizeof와 alignof가 underlying type과 같아야 한다는 문구가 표준상에 없음. 그래서 std::byte는 language defect임. 얘는 unsigned char가 underlying type인 enum인데, sizeof alignof가 1이라는 보장이 없거든
제정신인 표준 라이브러리 구현자라면 std::byte sizeof alignof을 1로 할 테니까 나중엔 std::byte 한정 예외문구가 표준상에 들어갈거임.
하지만, 모든 enum class는 underlying type을 char, unsigned char로 해도 그 sizeof가 1이 된다는 보장이 없다는 거는 염두하면 좋음. 이건 의도된 거임. 표준에 그걸 같게 하려는 문구가 들어갔다가 빠진 거니까.
std::byte 이야기는 처음 알았네 이것저것 알려줘서 ㄱㅅㄱㅅ
아니. 뭐 그딴.
아 enum class에서 상속이 저런 의미구나 - dc App
와 이건 좀 괴담이네
크아악