std::to_underlying是C++23新增的constexpr函数模板,用于将枚举值安全转为其显式指定的底层整数类型,仅接受枚举值(非类型名),等价于static_cast但更类型安全,不进行范围检查。

std::to_underlying 是 C++23 新增的工具,不是所有编译器都支持
如果你在 g++ 或 clang++ 下编译时报错 “‘to_underlying’ is not a member of ‘std’”,大概率是因为标准库还没跟上 C++23。libstdc++(GCC 默认)直到 GCC 13 才开始部分支持 std::to_underlying,而 libc++(Clang 默认)在 LLVM 17+ 中才提供完整实现。MSVC 从 19.35(VS 2022 17.5)起支持,但需显式开启 /std:c++23。
- 确认编译器版本:运行
g++ --version或clang++ --version - 启用 C++23 标准:加编译选项
-std=c++23(GCC/Clang)或/std:c++23(MSVC) - 头文件只需
#include <utility></utility>,无需额外引入枚举定义头
只能用于有固定底层类型的枚举(enum class 或带 : T 的 enum)
std::to_underlying 接受一个枚举值,返回其底层整型值;但它不接受传统无作用域枚举(plain enum),也不接受未指定底层类型的 enum class(某些老编译器会隐式推导,但标准要求显式声明)。
- ✅ 正确用法:
enum class E : int { A = 1 }; auto x = std::to_underlying(E::A); - ❌ 编译失败:
enum E { A }; std::to_underlying(E::A);(缺少底层类型) - ❌ 运行时未定义:
enum class E { A }; std::to_underlying(E::A);(C++23 要求必须显式指定底层类型) - 底层类型可以是
char、short、int、unsigned long long等可提升整型,但不能是bool或浮点类型
和 static_cast 的行为一致,但更类型安全
它本质就是对枚举值做一次 static_cast 到其底层类型,但封装后避免了手动写错目标类型的错误。比如枚举底层是 long long,你误写成 static_cast<int>(e)</int> 可能截断;而 std::to_underlying(e) 自动匹配底层类型。
- 等价关系:
std::to_underlying(e) ≡ static_cast<:underlying_type_t>>(e)</:underlying_type_t> - 不改变值:不会做符号扩展或零扩展以外的转换,负值保持负,高位保留
- 不检查范围:如果枚举值超出底层类型表示范围(极少发生),行为仍是未定义,它不做校验
- 性能无开销:纯编译期转换,生成的汇编和
static_cast完全一样
替代方案:C++20 及更早版本怎么安全取值
如果无法升级到 C++23,又想避免手写 static_cast 的隐患,可以自己写个轻量封装:
立即学习“C++免费学习笔记(深入)”;
template <typename E>
constexpr std::underlying_type_t<E> to_underlying(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
这个模板和标准版语义一致,但注意:std::underlying_type_t 对 plain enum 有效,而标准 std::to_underlying 不接受 plain enum —— 这是关键差异。若你项目里混用两种枚举,得自己判断是否允许 plain enum。
真正容易被忽略的是:即便用了 std::to_underlying,也不能绕过枚举的类型安全设计初衷。把它当“解包工具”用没问题,但别用它去干 reinterpret_cast 的事,比如把值塞进非对应枚举类型里再 cast 回来 —— 那已经超出枚举值合法范围,UB 就藏在那里。


















