c++oding="utf-8" ?>
std::to_underlying是c++23新增的constexpr函数,用于安全转换枚举值为其底层整型,需编译器支持c++23标准(如gcc 13+、clang 17+、msvc 19.35+)并启用-std=c++23;旧标准须用static_cast或自定义等价实现。

std::to_underlying 在 C++23 才可用,别在旧标准里找它
这个函数根本不是 C++20 或更早版本的标准组件。如果你在 GCC 12、Clang 15 或 MSVC 19.32 之前编译时看到 std::to_underlying 报错“not declared in this scope”,不是你写错了,是编译器还没实现 C++23。确认方式很简单:__cplusplus 宏值需 ≥ 202302L,且必须开启 -std=c++23(或 -std=gnu++23)。
手动实现 to_underlying 兼容 C++17/C++20
绝大多数项目还没切 C++23,但又需要安全取枚举值,直接用 static_cast 风险高(未定义行为可能发生在非底层类型可表示的值上)。推荐用以下带 SFINAE 的轻量实现:
template <typename e>
constexpr std::underlying_type_t<e> to_underlying(E e) noexcept {
return static_cast<:underlying_type_t>>(e);
}
</:underlying_type_t></e></typename>
使用时和标准版一致:to_underlying(MyEnum::Value)。注意点:
- 仅适用于 scoped(
enum class)和 unscoped(enum)枚举,不支持enum struct以外的变体(实际就是所有合法枚举) - 如果枚举值超出底层类型的表示范围(比如
enum : char { x = 300 };),转换结果是实现定义的,但至少不会 UB - 函数名建议不要用
std::前缀,避免与未来标准冲突
为什么不能直接 static_cast(e)?
看似简单,但有三个实际踩坑点:
- 底层类型不一定是
int:enum : char { a }的底层类型是char,强转成int会丢失符号信息(若char是 signed 且值为负) - 无作用域枚举隐式转换到整型:
enum Color { red }; int x = red;合法,但类型不明确,模板推导失败 - 模板元编程中无法泛化:你没法写
template<typename t> f(T e)</typename>然后统一处理,因为不知道该转成啥类型
用 std::underlying_type_t<e></e> 或等效手动实现,才能真正匹配枚举声明时指定的底层类型。
MSVC / Clang / GCC 对 C++23 to_underlying 的支持现状
截至 2024 年中:
- MSVC 19.35+(VS 2022 17.5+)已完整支持
<utility></utility>中的std::to_underlying - Clang 17+ 默认启用,但需
-std=c++23;Clang 16 需加-fexperimental-library - GCC 13+ 支持,但 libstdc++ 需要 13.2+ 才稳定;GCC 12 不支持,哪怕开了
-std=c++2b
跨平台项目如果坚持用标准版,得在构建系统里检查编译器版本和标准库版本,比手写一个 5 行函数麻烦得多。
真正容易被忽略的是:即使编译通过,也要确认枚举定义里没用 constexpr 以外的非常量表达式初始化值——std::to_underlying 要求参数是常量表达式,否则编译失败,而手动实现版没有这限制。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











