C++ – Clever use of sizeof to determine function overload matching

When implementing TypeTraits, I discovered that std::is_scoped_enum of the standard library will only be officially installed in C++23. So I found a clever alternative by looking through the source code of Unreal Engine 4, which is similar to the following implementation:

NAMESPACE_PRIVATE_BEGIN

uint8(&Resolve(int64))[2];
uint8 Resolve(...);

template <typename T>
struct TIsEnumConvertibleToInt : TBoolConstant<sizeof(Resolve(T())) - 1> { };

NAMESPACE_PRIVATE_END

template <typename T>
struct TIsScopedEnum : TBoolConstant<TAnd<TIsEnum<T>, TNot<NAMESPACE_PRIVATE::TIsEnumConvertibleToInt<T>>>::Value> { };

Through function name analysis, we can see that the basic principle is to distinguish between strong type Enum and weak type Enum by judging whether Enum can be implicitly converted to int64, focusing on the value of sizeof. Two functions are defined at the beginning, two overloads of Resolve, one of which accepts int64 as a parameter and the return value is uint8(&)[2] type, and the other accepts any other type and the return value is uint8, and then passes Falsely call the function, and then use sizeof to get the size of its return value. Because sizeof ignores modifiers such as CVRef, the judgment for uint8(&)[2] is equivalent to uint8[2], and the result is 2, while for uint8 the result is 1, and then maps it to true and false respectively through the -1 operation, thus completing the function overload matching diagnosis, thereby realizing the determination of Enum.

When studying Resolve(int64), I tried to get the return value type through std::invoke_result, but the result on MSVC was uint8[2], and the correct result should be uint8(&)[2] implemented by GCC. I was misled by this. For a long time.

Post Reply