支持相等比较的类的constexpr ID

constexpr ID for class supporting equality comparison

本文关键字:ID constexpr 比较 支持      更新时间:2023-10-16

我需要为我的C++类创建一个唯一的constexpr ID。进一步的要求是,这些ID应具有相等性(如果它们可以与><进行比较,则会更好)

脑海中浮现出一个简单的解决方案:

template <typename T>
struct IDMaker {
    static int i;
    static constexpr void * id = &i;
}

这种方法的问题在于以下内容无法在constexpr上下文中编译:

IDMaker<int>::id == IDMaker<double>::id;

id似乎是constexpr,但它的比较不是。我已经对此进行了一些实验,在constexpr上下文中使用这个id基本上没有什么有用的。(编辑:这是GCC 5.3中的一个错误,根据评论中的dyp,clang对此没有抱怨。这是GCC错误吗?还是clang过于宽容?)

为了提供一点上下文,我需要能够使用constexpr函数进行元编程,这样我就可以在运行时重用相同的逻辑。例如,我希望能够编写一个函数,该函数采用两个类型列表,并返回一个数组,该数组包含第二个列表中与第一个列表中的元素匹配的元素的索引。如果我能获得类的ID,我就可以很容易地将其实现为接收两个类型ID数组的constexpr函数。除了在运行时重用constexpr函数外,我希望这也能加快我的编译时间。

事实上,我有一个解决方案,但它涉及到一些宏观魔术;本质上,我使用宏来反映类和名称空间的名称,因为constexpr const char * name(){return #NewType;}我还可以访问类名称空间的反射类并获取其名称。我最终编写了一个constexpr散列函数:

constexpr size_t hash(const char *str) {
    size_t result{0};
    for(size_t i =0; str[i]!=0; ++i) {
        ...
    }
    return result;
}

有了这个函数,我可以散列一个类的完整名称,并获得一个constexprsize_t,它就工作了。不过,我对这种方法有两个问题:

1) 当我有1000个类时,我的编译时间会有多糟糕?你能推荐一个快速散列函数吗?2) 如何避免哈希冲突,或者至少在冲突发生时得到早期错误?

要在编译时为类生成ID,可以使用指向函数的指针。我把它实现为一个模板元函数,它返回一个指向函数的指针,并把一个类型作为参数:

template<typename T>
void id_gen(){}
using type_id_t = void(*)(void);
template<typename T>
constexpr type_id_t type_id = &id_gen<T>;

由于函数id_gen的每个实例化都有不同的地址,我们可以将其用作唯一标识符!

然后,您可以为您的类制作一些语法糖:

template<typename Crtp>
struct Identified {
    static constexpr type_id_t ID = type_id<Crtp>;
};

现在要识别您的类,您只需要使用继承:

struct A : Identified<A> {};
struct B : Identified<B> {};

此解决方案的优点:

  • 无碰撞
  • 非常快的编译时间
  • 易于在std::map中使用

此解决方案的缺点:

  • ID生成仅在编译时进行
  • 你无法预测这些值