C++中如何将大型稀疏枚举类映射到数组索引?
针对稀疏枚举类的快速查找方案
下面提供几种可行的方案,既能避免稀疏枚举直接映射带来的内存浪费,又能实现快速查找:
方案1:使用哈希表(std::unordered_map)
直接用枚举值作为键,PeopleInfo作为值存储,内存仅占用实际存在的枚举成员对应的对象空间,平均查找时间为O(1)。
#include <unordered_map> #include <stdexcept> #include <string> enum class People { American_start = 0x0, John, Aaron, Asian_start = 0x10000, Yen_Huan, Kang_Hsuan, European_start = 0x20000, // 其他枚举成员 }; struct PeopleInfo { // 根据需求定义成员,比如姓名、地区等 std::string name; }; // 初始化哈希表,绑定枚举值与对应信息 std::unordered_map<People, PeopleInfo> people_map = { {People::John, {"John"}}, {People::Aaron, {"Aaron"}}, {People::Yen_Huan, {"Yen Huan"}}, {People::Kang_Hsuan, {"Kang Hsuan"}}, // 其他枚举成员的映射关系 }; PeopleInfo fetch(People e) { auto iter = people_map.find(e); if (iter != people_map.end()) { return iter->second; } // 处理无效枚举值的情况,可根据需求返回默认值或抛出异常 throw std::invalid_argument("Invalid People enum value"); }
优缺点:实现简单,无需手动维护索引;但哈希表存在一定的运行时开销,且内存占用略大于紧凑数组。
方案2:枚举值→索引映射 + 紧凑数组
先给每个枚举成员分配连续的索引,通过哈希表将枚举值转换为索引,再用索引访问大小等于枚举成员总数的紧凑数组。
#include <unordered_map> #include <array> #include <stdexcept> #include <string> enum class People { American_start = 0x0, John, Aaron, Asian_start = 0x10000, Yen_Huan, Kang_Hsuan, European_start = 0x20000, // 其他枚举成员 }; struct PeopleInfo { std::string name; }; // 枚举值到紧凑数组索引的映射 const std::unordered_map<People, size_t> enum_to_index = { {People::John, 0}, {People::Aaron, 1}, {People::Yen_Huan, 2}, {People::Kang_Hsuan, 3}, // 按顺序为每个枚举成员分配唯一索引 }; // 紧凑数组,大小等于实际枚举成员的数量 const std::array<PeopleInfo, 4> people_arr = { {"John"}, {"Aaron"}, {"Yen Huan"}, {"Kang Hsuan"}, // 数组元素顺序需与enum_to_index的索引严格对应 }; PeopleInfo fetch(People e) { auto iter = enum_to_index.find(e); if (iter != enum_to_index.end()) { return people_arr[iter->second]; } throw std::invalid_argument("Invalid People enum value"); }
优缺点:数组访问是纯O(1)操作,比哈希表更快;但需要手动维护索引与数组元素的对应关系,新增枚举成员时要同步更新两处内容。
方案3:编译期生成映射表(C++17及以上)
利用C++编译期特性,提前生成枚举值到索引的映射和紧凑数组,运行时通过switch(编译器会优化为跳转表)实现无额外开销的快速查找,完全避免内存浪费。
#include <array> #include <tuple> #include <stdexcept> #include <utility> #include <string> enum class People { American_start = 0x0, John, Aaron, Asian_start = 0x10000, Yen_Huan, Kang_Hsuan, European_start = 0x20000, // 其他枚举成员 }; struct PeopleInfo { std::string name; }; // 编译期存储枚举值与对应信息的列表 constexpr auto people_list = std::make_tuple( std::pair{People::John, PeopleInfo{"John"}}, std::pair{People::Aaron, PeopleInfo{"Aaron"}}, std::pair{People::Yen_Huan, PeopleInfo{"Yen Huan"}}, std::pair{People::Kang_Hsuan, PeopleInfo{"Kang Hsuan"}} // 新增枚举成员时仅需在此处添加 ); // 编译期获取枚举值对应的数组索引 template<People E, size_t Index = 0> constexpr size_t get_enum_index() { if constexpr (Index == std::tuple_size_v<decltype(people_list)>) { return static_cast<size_t>(-1); // 标记为无效索引 } else if constexpr (std::get<0>(std::get<Index>(people_list)) == E) { return Index; } else { return get_enum_index<E, Index + 1>(); } } // 编译期生成紧凑数组 constexpr std::array<PeopleInfo, std::tuple_size_v<decltype(people_list)>> people_arr = [](){ std::array<PeopleInfo, std::tuple_size_v<decltype(people_list)>> arr{}; auto fill_array = [&]<size_t... Indices>(std::index_sequence<Indices...>) { ((arr[Indices] = std::get<1>(std::get<Indices>(people_list))), ...); }; fill_array(std::make_index_sequence<std::tuple_size_v<decltype(people_list)>>{}); return arr; }(); PeopleInfo fetch(People e) { switch(e) { case People::John: return people_arr[get_enum_index<People::John>()]; case People::Aaron: return people_arr[get_enum_index<People::Aaron>()]; case People::Yen_Huan: return people_arr[get_enum_index<People::Yen_Huan>()]; case People::Kang_Hsuan: return people_arr[get_enum_index<People::Kang_Hsuan>()]; // 新增枚举成员时同步添加case default: throw std::invalid_argument("Invalid People enum value"); } }
优缺点:运行时查找开销极小(接近直接数组访问),内存占用最优;但需要C++17及以上支持,代码复杂度稍高。
内容的提问来源于stack exchange,提问作者Shih-Chan Huang
相关产品推荐
相关产品推荐

