如何基于枚举类参数定义C++模板函数签名?
如何让模板函数的参数类型依赖于枚举类模板参数?
应用程序需要维护不同类的实体集合,以及实体之间的映射关系,这些映射通过结构体成员来编码,示例代码如下:
#include <unordered_set> using std::unordered_set; struct Entity1; struct Entity2; struct Entity { }; struct Entity1 : public Entity { Entity1* m_parent; // 第一个映射的源 Entity2* m_owner; // 第二个映射的源 unordered_set<Entity1*> m_children; // 第一个映射的目标 }; struct Entity2 : public Entity { unordered_set<Entity1*> m_elements; // 第二个映射的目标 };
为了编写映射相关的通用流程,需要明确映射的源类型(Dom)和目标类型(Ran)。我尝试了以下非法代码来定义这种类型关联:
enum class Mapping { M1, M2 }; // 以下代码不合法 template <Mapping r> Sig { typename Dom; typename Ran; }; using Sig.Dom<Mapping::M1> = Entity1; using Sig.Ran<Mapping::M1> = Entity1; using Sig.Dom<Mapping::M2> = Entity1; using Sig.Ran<Mapping::M2> = Entity2;
期望实现的通用流程示例如下,通过枚举模板参数自动推导参数类型:
template <Mapping r> void link(Sig.Dom<r>* obj1, Sig.Ran<r>* obj2); template<> void link<Mapping::M1>(Entity1* obj1, Entity1* obj2) { if (obj1->m_parent != nullptr) { obj1->m_parent->m_children.erase(obj1); } obj1->m_parent = obj2; if (obj1->m_parent != nullptr) { obj1->m_parent->m_children.insert(obj1); } } template<> void link<Mapping::M2>(Entity1* obj1, Entity2* obj2) { if (obj1->m_owner != nullptr) { obj1->m_owner->m_elements.erase(obj1); } obj1->m_owner = obj2; if (obj1->m_owner != nullptr) { obj1->m_owner->m_elements.insert(obj1); } }
请问在C++中是否有可行的实现方式?
当然可以实现,核心思路是利用模板特化来为每个枚举值绑定对应的源类型和目标类型,具体实现步骤如下:
- 定义枚举类和基础模板结构体
首先定义枚举类Mapping,然后声明一个模板结构体MappingTraits,它接受枚举值作为模板参数,内部预留Dom和Ran两个类型成员:
enum class Mapping { M1, M2 }; template <Mapping r> struct MappingTraits;
- 特化模板结构体,绑定类型
针对每个枚举值,特化MappingTraits结构体,明确指定对应的Dom(源类型)和Ran(目标类型):
template<> struct MappingTraits<Mapping::M1> { using Dom = Entity1; using Ran = Entity1; }; template<> struct MappingTraits<Mapping::M2> { using Dom = Entity1; using Ran = Entity2; };
- 编写通用模板函数
现在可以基于MappingTraits来定义通用的link函数,通过模板参数r自动获取对应的源类型和目标类型:
template <Mapping r> void link(typename MappingTraits<r>::Dom* obj1, typename MappingTraits<r>::Ran* obj2);
- 特化实现具体逻辑
最后为每个枚举值特化link函数,实现对应的映射维护逻辑:
template<> void link<Mapping::M1>(Entity1* obj1, Entity1* obj2) { if (obj1->m_parent != nullptr) { obj1->m_parent->m_children.erase(obj1); } obj1->m_parent = obj2; if (obj1->m_parent != nullptr) { obj1->m_parent->m_children.insert(obj1); } } template<> void link<Mapping::M2>(Entity1* obj1, Entity2* obj2) { if (obj1->m_owner != nullptr) { obj1->m_owner->m_elements.erase(obj1); } obj1->m_owner = obj2; if (obj1->m_owner != nullptr) { obj1->m_owner->m_elements.insert(obj1); } }
这种写法完全符合C++语法规范,不仅实现了通过枚举模板参数推导函数参数类型的需求,还保持了代码的可扩展性——后续新增映射类型时,只需添加对应的枚举值和MappingTraits特化即可。
如果使用C++17及以上版本,还可以借助inline constexpr或auto模板参数进一步简化代码,但上述方案是兼容性最好、逻辑最清晰的实现方式。
内容的提问来源于stack exchange,提问作者dde
相关产品推荐
相关产品推荐

