带枚举模板参数的模板类如何实现免修改的工厂函数?
实现方案
核心思路利用编译期生成所有枚举值对应的构造函数映射,后续新增枚举仅需修改枚举定义本身,无需调整工厂相关代码。
最优方案(枚举连续场景)
该方案要求枚举默认从0开始连续赋值(C++枚举默认行为),仅需在枚举末尾新增一个count标记项即可,运行时效率最高。
步骤1:修改Colour枚举定义
enum class Colour { red, blue, orange, count // 所有新增枚举值必须放在该行之前 };
原有PencilBase和Pencil模板类无需任何修改。
步骤2:实现工厂逻辑
#include <utility> #include <array> #include <stdexcept> #include <memory> // 编译期生成所有颜色对应的铅笔构造函数数组 template <std::size_t... Is> constexpr auto build_factory_array(std::index_sequence<Is...>) { return std::array{ []() -> std::unique_ptr<PencilBase> { return std::make_unique<Pencil<static_cast<Colour>(Is)>>(); }... }; } // 全局工厂数组,编译期完成初始化 constexpr auto g_pencil_factory = build_factory_array( std::make_index_sequence<static_cast<std::size_t>(Colour::count)>{} ); // 对外暴露的工厂函数 std::unique_ptr<PencilBase> createColourPencil(Colour c) { const auto idx = static_cast<std::size_t>(c); if (idx >= g_pencil_factory.size()) { throw std::invalid_argument("无效的颜色枚举值"); } return g_pencil_factory[idx](); }
这里用
std::unique_ptr替代裸指针避免内存泄漏,如需返回裸指针直接调整返回类型和构造逻辑即可。
方案优势
- 运行时效率极高,直接数组索引调用,无任何查找开销
- 新增枚举仅需在
Colour::count前添加枚举项,完全无需修改工厂代码 - 编译期完成所有逻辑生成,无额外运行时初始化开销
注意事项
- 不要手动为枚举成员赋值,保持默认从0连续递增的规则
- 兼容C++14及以上标准
可选方案(枚举不连续场景)
如果枚举值不连续,或不想添加count标记,可以用自动注册机制,缺点是需要确保所有用到的Pencil<Colour>模板特化都被显式实例化,否则运行时会找不到对应构造函数。
实现代码
#include <map> #include <functional> #include <stdexcept> #include <memory> // 全局注册表 struct PencilRegistry { using Creator = std::function<std::unique_ptr<PencilBase>()>; static std::map<Colour, Creator>& get() { static std::map<Colour, Creator> instance; return instance; } template <Colour c> struct RegisterHelper { RegisterHelper() { get()[c] = []() { return std::make_unique<Pencil<c>>(); }; } }; }; // 在Pencil模板类中添加静态注册成员 template <Colour c> class Pencil : public PencilBase { void paint() override { // use c } inline static PencilRegistry::RegisterHelper<c> _register{}; }; // 工厂函数实现 std::unique_ptr<PencilBase> createColourPencil(Colour c) { auto& registry = PencilRegistry::get(); auto it = registry.find(c); if (it == registry.end()) { throw std::invalid_argument("无效的颜色枚举值"); } return it->second(); }
内容的提问来源于stack exchange,提问作者MojoRisin
相关产品推荐
相关产品推荐

