std::is_scoped_enum_v可编译期判断T是否为enum class:对enum class返回true,对普通enum及非枚举类型返回false;需用_v后缀或::value获取布尔值,不可直接使用类型特征本身。

怎么用 std::is_scoped_enum 检查枚举是否为 scoped(即 enum class)
std::is_scoped_enum 是 C++23 引入的类型特征,用于在编译期判断一个类型是否为作用域限定枚举(enum class 或 enum struct)。它返回 std::true_type / std::false_type,不能直接用于 static_assert 或 requires,必须取 ::value 或用 constexpr bool 展开。
常见错误是写成 static_assert(std::is_scoped_enum<t>);</t> —— 这会失败,因为类型特征本身不是布尔值。
- 正确写法:
static_assert(std::is_scoped_enum_v<t>);</t>(推荐,C++17 起可用_v后缀) - 或显式写:
static_assert(std::is_scoped_enum<t>::value);</t> - 注意:普通
enum(未加class)返回false;enum class E { a };返回true - 空枚举(
enum class E {};)也满足std::is_scoped_enum_v<e></e>,这是合法的
在模板中用 requires 限制只能传入 scoped enum
如果你用的是 C++20,requires 是最直观的约束方式。但要注意:约束条件必须能被编译器静态求值,且不能依赖未定义行为。
典型误写:requires std::is_scoped_enum<t></t> —— 错,这传的是类型,不是布尔值。
立即学习“C++免费学习笔记(深入)”;
- 正确写法:
requires std::is_scoped_enum_v<t></t> - 完整示例:
template<typename T> concept ScopedEnum = std::is_scoped_enum_v<T>; <p>template<ScopedEnum T> void process(T e) { /<em> ... </em>/ } - 如果不用 concept,也可用 SFINAE:
template<typename t std::enable_if_t>, int> = 0></typename> - 注意:
std::is_scoped_enum_v对非枚举类型(如int、struct)安全返回false,不会导致硬错误
为什么不能用 std::is_enum 替代
std::is_enum_v<t></t> 会同时匹配 enum 和 enum class,而实际需求常是「只接受强类型枚举」——比如要避免 ADL 冲突、隐式整型转换或名字污染。
- 例如:
enum Color { red, blue };和enum class Size { small, large };都满足std::is_enum_v,但前者会把red注入外层作用域,后者不会 - 若模板内部依赖枚举的封闭性(如用
switch+[[fallthrough]],或做位运算封装),混入 unscoped enum 可能引发静默错误 -
std::is_scoped_enum是唯一能精确区分二者的标准 trait,别试图用std::underlying_type+ 名字查找模拟
兼容性与替代方案(C++20/23 之前)
如果你还在用 C++17 或更早版本,std::is_scoped_enum 不可用。此时没有标准办法可靠检测 scoped enum,所有 workaround 都有缺陷。
- 常见 hack:
!std::is_convertible_v<t std::underlying_type_t>></t>—— 但 unscoped enum 也常不可隐式转为 underlying type(如带explicit构造函数的包装类也会误报) - 更糟的是:某些编译器对空
enum class的std::underlying_type处理不一致,可能 SFINAE 失效 - 真正可靠的降级做法是:放弃自动检测,改用显式标记,比如要求用户继承某个空基类或提供
using is_scoped_enum = std::true_type; - C++23 是分水岭,只要能升级标准,就别自己造轮子
最易被忽略的一点:即使用了 std::is_scoped_enum_v,也要小心模板实参推导——比如传入 decltype(e) 时,若 e 是 const 或引用,需先 std::remove_cvref_t,否则 trait 判定失败。


















