发布于2026-07-17 阅读(0)
扫一扫,手机访问
聊一聊C++23里一个很实用的工具——std::to_underlying。它专门用来把枚举值安全、类型明确地转成它底层的整数类型。注意,它是一个constexpr函数模板,不是宏,也不是模板别名。使用时必须传进来的是枚举值,而不是枚举类型或者类型名。废话不多说,直接切正题。
最常踩的坑是什么?就是把它当std::underlying_type用。比如写成std::to_underlying或者std::to_underlying(Status),都会直接编译失败。为什么?因为它的参数是一个值(参数类型是 E),不是类型(不是 typename E)。
正确的用法只有一种:传一个已经定义好的枚举变量或者字面量。看几个例子就明白了:
enum class Color : uint8_t { Red = 1 }; 然后 auto v = std::to_underlying(Color::Red); —— 这个 v 的类型就是 uint8_t,没问题。Color c = Color::Red;,那么 auto x = std::to_underlying(c); 同样有效。std::to_underlying(42); 会编译错误,因为42不是枚举值。std::to_underlying; 也是错的,语法根本就不对。但千万别误会,它虽然叫“安全转换”,实际上并不做任何范围检查。它只是原封不动地把枚举值转发到它的底层类型,不会校验是否溢出。一旦底层类型装不下你塞进去的值,行为就是未定义的。
具体来说:
enum class Big : char { X = 300 };,然后 auto x = std::to_underlying(Big::X); —— 这里 char 通常最大只能到127,所以结果是未定义行为(UB)。static_cast 检查一下范围,或者自己实现一个 std::is_valid_enum 来兜底。std::to_underlying 在安全性上跟 static_cast 完全等价,别以为它是更安全的替代品。说到这,就不得不提一下它和 std::underlying_type_t 的关系。这两者分工明确,互不替代:一个处理值,一个处理类型。混着用就容易逻辑混乱。
std::underlying_type_t 得到的是类型(比如 uint8_t),用来声明变量、做模板参数、算 sizeof 什么的。std::to_underlying(Color::Red) 得到的是值(比如 1),用在计算、序列化、日志输出这些需要实际数值的场景。template constexpr auto to_underlying(E e) { return static_cast>(e); } 。这个写法从C++11就能用,旧编译器也支持,比 std::to_underlying 更早可用。最后提醒一个最容易忽略的点:如果你的枚举是前向声明的——比如 enum class E;,而定义在别处——那么 std::to_underlying 没法实例化,因为底层类型未知。这意味着它不能用在那种只靠前向声明就能用的接口设计里。这点在实际编码中很容易翻车,得留个心眼。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8