std::disjunction模板短路失效?枚举底层类型编译期校验问询
解决编译期校验枚举底层类型为
std::int32_t的问题 你的问题核心在于:模板参数的实例化优先级高于逻辑短路判断,哪怕std::disjunction或者||有语义上的短路,编译器在处理模板参数时会先实例化所有用到的 trait,包括非枚举类型下的std::underlying_type_t,这直接触发了编译器错误——因为这个 trait 只允许枚举类型作为输入。
为什么原来的方案失败?
std::disjunction的短路是指求值逻辑上的短路,但模板参数的实例化是在逻辑判断之前发生的。当TestedType不是枚举时,std::underlying_type_t<TestedType>会被强制实例化,违反了该 trait 的使用条件,导致编译报错。布尔运算符||也是同样的道理,两边的模板表达式都会被实例化,无法避免非法调用。
正确的编译期实现方案
方案1:用SFINAE编写辅助Trait
通过SFINAE(Substitution Failure Is Not An Error)特性,我们可以针对枚举类型和非枚举类型分别处理,避免在非枚举类型下实例化std::underlying_type_t:
#include <cstdint> #include <type_traits> // 默认版本:非枚举类型直接返回true template<typename T, typename = void> struct is_enum_with_int32_underlying : std::true_type {}; // 特化版本:仅当T是枚举类型时生效,检查底层类型 template<typename T> struct is_enum_with_int32_underlying<T, std::enable_if_t<std::is_enum_v<T>>> : std::is_same<std::underlying_type_t<T>, std::int32_t> {}; // 简化使用的constexpr变量 template<typename TestedType> constexpr bool nonEnumOrIntBacked = is_enum_with_int32_underlying<TestedType>::value; // 测试用例 enum class ValidEnum : std::int32_t { A, B }; enum class InvalidEnum : std::int64_t { X, Y }; using NonEnumType = std::int32_t; static_assert(nonEnumOrIntBacked<ValidEnum>, "ValidEnum必须通过校验"); static_assert(!nonEnumOrIntBacked<InvalidEnum>, "InvalidEnum应该触发校验失败"); static_assert(nonEnumOrIntBacked<NonEnumType>, "非枚举类型直接通过校验");
方案2:用C++17的if constexpr(更简洁)
如果你使用的VS2017支持C++17(需要开启相应编译选项),if constexpr可以在编译期直接分支,避免非法的 trait 实例化:
#include <cstdint> #include <type_traits> template<typename TestedType> constexpr bool is_valid_type() { if constexpr (std::is_enum_v<TestedType>) { // 仅当TestedType是枚举时,才实例化std::underlying_type_t return std::is_same_v<std::underlying_type_t<TestedType>, std::int32_t>; } else { return true; } } // 测试用例 enum class ValidEnum : std::int32_t { A, B }; enum class InvalidEnum : std::int64_t { X, Y }; using NonEnumType = std::int32_t; static_assert(is_valid_type<ValidEnum>(), "ValidEnum必须通过校验"); static_assert(!is_valid_type<InvalidEnum>(), "InvalidEnum应该触发校验失败"); static_assert(is_valid_type<NonEnumType>(), "非枚举类型直接通过校验");
总结
完全不需要改用运行时检查,这两个方案都能在编译期完成校验,且完美适配你的需求:非枚举类型直接通过,枚举类型则强制要求底层类型为std::int32_t。其中if constexpr的写法更直观易读,推荐优先使用(只要编译器支持C++17)。
内容的提问来源于stack exchange,提问作者bionicOnion
相关产品推荐
相关产品推荐

