在实现 TypeTraits 时发现标准库的 std::is_scoped_enum 在 C++23 才会正式实装,于是通过翻 Unreal Engine 4 的源码发现了一个巧妙的替代方案,类似如下的实现:
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> { };
通过函数名分析,可以看出其基本原理是通过判断 Enum 是否能隐式转换为 int64 来区分 强类型 Enum 和 弱类型 Enum ,重点是通过 sizeof 的值。在最开头定义了两个函数, Resolve 的两个重载,其中一个接受 int64 为参数,返回值为 uint8(&)[2] 类型,另一个接受任意的其他类型,返回值为 uint8 ,之后通过假调用函数,然后使用 sizeof 得到其返回值的大小,因为 sizeof 会忽略 CVRef 等修饰符,所以对于 uint8(&)[2] 的判定等同于 uint8[2] ,其结果为 2 ,而对于 uint8 结果为 1 ,再通过 – 1 操作分别将其映射为 true 和 false ,就完成了函数重载匹配诊断,以此实现对 Enum 的判定。
研究 Resolve(int64) 时,试图通过 std::invoke_result 拿到返回值类型,但是 MSVC 上的结果为 uint8[2] ,而正确的结果应该为 GCC 实现的 uint8(&)[2] ,被此误导了好久。