C++20标准下,能否合法将无作用域枚举底层类型指针转为枚举指针?
C++20中无作用域枚举与底层类型指针的合法转换问题
问题核心
在C++20标准下,能否合法地将无作用域枚举类型的底层类型指针转换为该枚举类型的指针?例如实现如下函数且避免未定义行为(最好无实现定义行为):
enum Enum : int { FOO = 0, BAR = 1, }; // 如何实现该函数且无未定义行为(最好无实现定义行为)? const Enum* ToEnum(const int* p);
背景需求是在模板API中,需要处理Protocol Buffers提供的proto2::RepeatedField<int>(本质是int数组),希望无需复制即可将其转为std::span<Enum>。
关键限制
直接使用reinterpret_cast进行转换并不符合标准规定的合法场景:
- 虽然无作用域枚举的对象表示与底层类型完全一致([dcl.enum]/8),且枚举类型与底层类型均为标准布局类型,但严格别名规则([basic.lval]/11)禁止通过枚举类型的glvalue访问底层类型的对象——因为枚举类型与底层类型不属于规则允许的互相别名类型(规则仅允许算术类型的有符号/无符号版本互相别名,不包含枚举与底层类型)。
- 即使
reinterpret_cast的指针转换操作在满足标准布局、对齐一致的条件下是定义良好的([expr.reinterpret.cast]/7),后续通过转换后的指针访问对象仍会触发未定义行为。
可行方案
1. 类型安全的视图转换(无UB,无需复制)
若模板API可以接受视图而非std::span,可使用std::views::transform创建一个实时转换的视图,无需复制数组:
#include <ranges> auto proto_span = repeated_field.AsSpan(); // 获取std::span<const int> auto enum_view = proto_span | std::views::transform([](int val) { return static_cast<Enum>(val); });
该视图会在访问元素时实时将int转换为Enum,完全符合标准,无任何未定义行为。
2. 依赖编译器扩展(非标准,需谨慎)
如果必须得到std::span<Enum>,部分主流编译器(如GCC、Clang、MSVC)在实际实现中允许枚举与底层类型互相别名(因为对象表示完全一致),此时可以使用reinterpret_cast:
const Enum* ToEnum(const int* p) { return reinterpret_cast<const Enum*>(p); } // 转换为span std::span<const Enum> enum_span(ToEnum(repeated_field.data()), repeated_field.size());
但需注意:这种方式在标准层面属于未定义行为,仅能依赖编译器的扩展支持,跨平台时存在风险。
3. 复制转换(完全标准,无风险)
如果性能允许,最稳妥的方式是复制数组并将每个元素转换为枚举类型:
#include <vector> #include <span> std::vector<Enum> enum_vec(repeated_field.begin(), repeated_field.end()); std::span<const Enum> enum_span(enum_vec);
这种方式完全符合标准,无任何未定义行为,但会产生内存复制开销。
内容的提问来源于stack exchange,提问作者jacobsa
相关产品推荐
相关产品推荐

