C++20如何遍历以枚举为模板参数的成员函数?
枚举模板参数成员函数的自动遍历方案
1. 完善枚举定义
首先给fruit枚举添加一个表示总数量的枚举值,同时补充数值转枚举的辅助函数,为后续遍历提供基础:
struct fruit { enum Value : uint8_t { apple, banana, kiwi, count // 新增:标记枚举值总数量 }; fruit() = default; fruit(Value afruit) : value(afruit) { } Value value; // 辅助:将索引值转换为对应的枚举值 static constexpr Value from_index(uint8_t idx) { return static_cast<Value>(idx); } };
2. 实现通用遍历逻辑
利用C++11的std::integer_sequence生成编译时索引序列,通过参数包展开自动调用每个枚举对应的模板成员函数:
#include <utility> // 引入std::integer_sequence namespace detail { // 内部实现:展开索引序列并执行操作 template <typename Obj, typename Func, std::size_t... Idx> void for_each_fruit_impl(Obj& obj, Func&& func, std::integer_sequence<std::size_t, Idx...>) { // 逗号表达式展开,对每个索引对应的枚举值执行传入的操作 (func(obj, fruit::from_index(static_cast<uint8_t>(Idx))), ...); } } // namespace detail // 对外接口:遍历所有fruit枚举值 template <typename Obj, typename Func> void for_each_fruit(Obj& obj, Func&& func) { using Seq = std::make_integer_sequence<std::size_t, static_cast<std::size_t>(fruit::count)>; detail::for_each_fruit_impl(obj, std::forward<Func>(func), Seq{}); }
3. 实际使用示例
遍历调用field::plant()
替换原来的手动逐个调用,用遍历逻辑自动处理所有枚举值:
int main(int argc, char** argv) { auto f = field{}; // 自动遍历所有fruit枚举值,调用对应的plant模板函数 for_each_fruit(f, [](auto& obj, fruit::Value f_val) { if constexpr (f_val == fruit::apple) { obj.plant<fruit::apple>(); } else if constexpr (f_val == fruit::banana) { obj.plant<fruit::banana>(); } else if constexpr (f_val == fruit::kiwi) { obj.plant<fruit::kiwi>(); } }); // 针对带参数的barn::store()函数 auto b = barn{}; int arg = 42; for_each_fruit(b, [arg](auto& obj, fruit::Value f_val) { if constexpr (f_val == fruit::apple) { obj.store<fruit::apple>(arg); } else if constexpr (f_val == fruit::banana) { obj.store<fruit::banana>(arg); } else if constexpr (f_val == fruit::kiwi) { obj.store<fruit::kiwi>(arg); } }); return 0; }
4. 优化:简化枚举值与模板参数的匹配
如果觉得每个枚举值写if constexpr繁琐,可以封装通用调用函数,直接映射枚举值到模板参数:
// 针对plant函数的通用调用 template <fruit::Value F, typename Obj> void call_plant(Obj& obj) { if constexpr (F == fruit::apple) { obj.template plant<fruit::apple>(); } else if constexpr (F == fruit::banana) { obj.template plant<fruit::banana>(); } else if constexpr (F == fruit::kiwi) { obj.template plant<fruit::kiwi>(); } } // 针对store函数的通用调用 template <fruit::Value F, typename Obj, typename... Args> void call_store(Obj& obj, Args&&... args) { if constexpr (F == fruit::apple) { obj.template store<fruit::apple>(std::forward<Args>(args)...); } else if constexpr (F == fruit::banana) { obj.template store<fruit::banana>(std::forward<Args>(args)...); } else if constexpr (F == fruit::kiwi) { obj.template store<fruit::kiwi>(std::forward<Args>(args)...); } }
之后遍历代码可以简化为:
// 调用plant for_each_fruit(f, [](auto& obj, fruit::Value f_val) { call_plant<f_val>(obj); }); // 调用store for_each_fruit(b, [arg](auto& obj, fruit::Value f_val) { call_store<f_val>(obj, arg); });
内容的提问来源于stack exchange,提问作者mmodel1
相关产品推荐
相关产品推荐

