I recently discovered that C++ Scoped enums are allowed to use bool
as the underlying type, which somewhat makes sense since std::is_integral_v<bool>
is true
.
However, I've also found that the truncation behavior when static_cast
ing to a type made this way causes different results across compilers. In particular:
enum class Option : bool {
No = false,
Yes = true,
};
constexpr auto v = static_cast<Option>(8);
std::cout << static_cast<bool>(v) << std::endl;
GCC reports this result as false
, whereas Clang and MSVC report true
(MSVC can be seen by checking the mov cl, 1
prior to calling).
This value reports as true
for GCC if the last bit is set, so static_cast<Option>(9)
will result in true
as the underlying type:
constexpr auto v = static_cast<Option>(9);
std::cout << static_cast<bool>(v) << std::endl;
It seems that GCC treats the static_cast
as a 1-bit unsigned integral value cast, and thus truncates the value to 1 bit, much like a uint32_t
would narrow to a uint8_t
, whereas Clang and MSVC both treat it like a normal boolean cast using n > 0
.
Which compiler here is correct?
If this is some form of undefined behavior, why can it be used in a constexpr
expression?
Note: An obvious workaround is to use static_cast<Option>(static_cast<bool>(...))
, but I'm curious about what behavior should be expected here -- if any.
This behavior appears to be occur in all C++ versions that support scoped enums (from c++11 and up).