如何根据枚举类参数定义模板函数签名?

How to define template function signature based on enum class parameter?

提问人:dde 提问时间:11/15/2023 更新时间:11/15/2023 访问量:69

问:

是否可以声明一个模板函数,使该函数的参数类型依赖于枚举类模板参数?

用例大致如下。应用程序维护不同类的实体的集合,以及这些实体之间的映射。映射编码为结构成员,如下所示:

#include<unordered_set>
using std::unordered_set;

struct Entity1;
struct Entity2;

struct Entity {
};

struct Entity1 : public Entity {
   Entity1* m_parent; // source of first mapping
   Entity2* m_owner; // source of first mapping
   unordered_set<Entity1*> m_children; // destination of first mapping
};

struct Entity2 : public Entity {
   unordered_set<Entity1*> m_elements; // destination of second mapping
};

为了对映射进行一些通用过程的编码,我希望能够识别映射的源和目标的类型。

enum class Mapping { M1, M2 };

// the following is illegal
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++ 中实现这一点?

C++ 模板

评论


答:

1赞 YSC 11/15/2023 #1

给你:

template <Mapping r>
struct LinkParameters
{};

template<>
struct LinkParameters<Mapping::M1>
{
    using First = Entity1*;
    using Second = Entity1*;
};
template<>
struct LinkParameters<Mapping::M2>
{
    using First = Entity1*;
    using Second = Entity2*;
};

template <Mapping r>
void link(typename LinkParameters<r>::First obj1, typename LinkParameters<r>::Second obj2)
{
    if constexpr (r == Mapping::M1) {
        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);
        }
    } else {
        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);
        }
    }
}

完整程序演示

这个想法是,要尽可能多地明确地对特质进行专门化。LinkParameterslink

对于 的定义,我决定使用解决方案来演示另一种方法。你只能决定什么是最好的,所有的东西大多是兼容的,所以要试验和选择。link()constexpr if

作为替代方法,可以是使用常用惯用语(全局定义 + 显式专用化)定义的可调用类型。由于它是一种类型,因此可以包含或嵌套该特征以进行更多封装。此解决方案保留为练习。linkLinkParameters

评论

0赞 dde 11/15/2023
谢谢,这正是所需要的。请注意,代码摘录的开头缺少一行:。template <Mapping r>
0赞 YSC 11/15/2023
确实^^固定的