std::is_scoped_enum 是 c++23 新增的类型特征,c++20 及更早版本需通过 std::is_enum_v 与 !std::is_convertible_v 组合 sfinae 模拟判断,用于序列化、反射等场景区分作用域枚举与无作用域枚举。

std::is_scoped_enum 在 C++23 之前不可用
它不是 C++11/14/17 的特性,而是 C++23 新增的类型特征。如果你用的是 GCC 12+、Clang 15+ 或 MSVC 19.35+ 且启用了 -std=c++23(或更高标准),才能直接使用 std::is_scoped_enum。否则编译会报错:error: 'is_scoped_enum' is not a member of 'std'。
常见误操作是查文档看到这个 trait 就直接写,结果在 CI 或老版本编译器上失败。确认方式很简单:
#include <type_traits> static_assert(std::is_scoped_enum_v<enum class e>); // C++23 only</enum></type_traits>
如何在 C++20 及更早版本中模拟判断
没有 std::is_scoped_enum 时,只能靠枚举是否可隐式转换为整型 + 是否有作用域限定符这两个行为组合推断。核心思路是:scoped enum 不能隐式转 int,且其底层类型不可默认访问(但可通过 decltype + static_cast 推导)。
一个轻量、无依赖的 SFINAE 实现如下:
template<typename t>
struct is_scoped_enum {
private:
template<typename u>
static auto check(int) -> decltype(static_cast<int>(U{}), std::true_type{});
template<typename>
static std::false_type check(...);
public:
static constexpr bool value = std::is_enum_v<t> && !std::is_convertible_v<t int> && decltype(check<t>(0))::value;
};</t></t></t></typename></int></typename></typename>
-
std::is_enum_v<t></t>先筛出所有枚举 -
!std::is_convertible_v<t int></t>排除 unscoped enum(它们能隐式转int) - 额外用
check确保类型确实支持static_cast<int></int>—— 避免某些自定义 operator 的干扰
区分 scoped enum 和 unscoped enum 的真实场景
最常需要区分的地方是序列化、反射、日志打印或模板元编程中对枚举值的泛化处理。比如你想为 scoped enum 自动加类名前缀输出,而 unscoped enum 直接输出数值:
template<typename e>
auto enum_name(E e) {
if constexpr (std::is_scoped_enum_v<e>) {
return std::string{typeid(E).name()} + "::" + std::to_string(static_cast<int>(e));
} else {
return std::to_string(static_cast<int>(e));
}
}</int></int></e></typename>
注意:typeid(E).name() 返回的是实现相关字符串(如 "N4main3FooE"),实际项目中建议配合宏或专用注册表;更重要的是,std::is_scoped_enum_v 在 constexpr if 中必须是编译期常量 —— 所以它只适用于已知类型,不能用于运行时类型擦除。
容易被忽略的兼容性陷阱
即使你用了 C++23,也要小心几个边界情况:
-
enum class E : char {}和enum class E {}都满足std::is_scoped_enum_v,但底层类型不同,影响序列化大小和 ABI 兼容性 - 空枚举
enum class Empty {}在部分旧编译器上可能不被完全支持,std::is_scoped_enum_v返回true,但static_cast<int>(Empty{})</int>是未定义行为 - 模板参数推导中,如果传入的是
E::Value(枚举成员),而非E(枚举类型本身),std::is_scoped_enum_v<decltype></decltype>才有效 —— 写成std::is_scoped_enum_v<:value></:value>会编译失败
真正麻烦的不是怎么写判断,而是后续逻辑是否真的适配了 scoped enum 的强类型约束 —— 比如忘记加 static_cast 就直接算术运算,或者误用 == 比较不同 scoped enum 类型的值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











