std::is_scoped_enum是c++23引入的特性,gcc 12/clang 14/msvc 19.30之前版本不支持,需通过__cpp_lib_is_scoped_enum宏检测,并可用std::is_enum_v与!std::is_convertible_v手动模拟。

std::is_scoped_enum 在 C++23 之前不可用
别急着写 std::is_scoped_enum —— 它是 C++23 引入的,如果你用的是 GCC 12/Clang 14 之前的版本,或者 MSVC 19.30 之前,默认不支持。编译会直接报错:error: 'is_scoped_enum' is not a member of 'std'。确认标准版本最简单的方式是检查 __cpp_lib_is_scoped_enum 宏:
#if __cpp_lib_is_scoped_enum
<p>若不满足,得自己实现或降级使用 <code>std::is_enum</code> + 手动判断(见下一条)。</p>
<h3>如何用 static_assert + std::is_scoped_enum 约束模板参数</h3>
<p>假设你希望一个函数模板只接受 <code>enum class</code>,拒绝 <code>enum</code> 和其他类型。直接用 <code>static_assert</code> 最清晰:</p>
<pre class="brush:php;toolbar:false;">template <typename t>
void process_enum(T) {
static_assert(std::is_scoped_enum_v<t>, "T must be a scoped enum");
// ...
}</t></typename>
注意三点:
-
std::is_scoped_enum_v<t></t>是变量模板,比std::is_scoped_enum<t>::value</t>更简洁 - 它对
enum class E { a, b };返回true,对enum U { x, y };返回false - 对非枚举类型(如
int、struct S {})也返回false,所以断言能拦住所有非法输入
兼容 C++17/20 的手动判定方法
如果必须支持旧标准,可以靠“scoped enum 是 enum 且不可隐式转换为整数”这一特性间接判断。但更稳妥的做法是结合 std::is_enum_v<t></t> 和 std::is_convertible_v<t int></t>:
template <typename t> constexpr bool is_scoped_enum_v = std::is_enum_v<t> && !std::is_convertible_v<t int>;</t></t></typename>
这个表达式在绝大多数情况下有效,但有例外:
- 如果某个
enum class显式声明了operator int(),它就变成可转换的,会被误判为非 scoped - 某些老编译器对
std::is_convertible处理不一致(尤其是涉及私有构造时),建议加static_assert验证典型用例 - 它不能区分
enum class和enum struct(二者等价),但这不是问题
为什么不能只用 std::is_enum_v?
std::is_enum_v<t></t> 对 enum 和 enum class 都返回 true,无法区分作用域行为。比如:
enum Legacy { A, B };
enum class Modern { X, Y };
static_assert(std::is_enum_v<legacy>); // true
static_assert(std::is_enum_v<modern>); // true</modern></legacy>
如果你的模板逻辑依赖强类型语义(比如禁止与整数混用、要求 ADL 查找限定名),仅靠 std::is_enum_v 就会放行 Legacy,导致后续操作出错。真正的约束点不在“是不是枚举”,而在“是不是带作用域的枚举”。
实际项目里最容易漏掉的是编译器版本检查——写了 std::is_scoped_enum 却没配 C++23 标准,或者没设 -std=c++23,结果 CI 上编译失败。先查宏,再写断言,比事后调试快得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











