이 코드를 최적화를 키고 컴파일 하면 어떤 어셈블리가 나올까? (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)


모든 enum에는 underlying type이 있다 (https://timsong-cpp.github.io/cppwp/n4868/enum#dcl.enum-5)
underlying type이란 이름에서 유추할 수 있듯이 enum의 값이 저장되는 타입이다
위의 boring enum 같은 경우에는 boring::a랑 boring::b가 각각 0과 1이란 값으로 저장될텐데 이 값들의 타입을 underlying type이라고 부른다
boring은 underlying type이 explicit하게 명시되어있지 않은 scoped enum이므로 underlying type은 int로 default되며 fixed underlying type을 가진다

여기서 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)


혹시 오류가 있으면 알려주면 수정함