支持相等比较的类的constexpr ID
constexpr ID for class supporting equality comparison
我需要为我的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;
}
有了这个函数,我可以散列一个类的完整名称,并获得一个constexpr
size_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生成仅在编译时进行
- 你无法预测这些值
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 多成员Constexpr结构初始化
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- Visual C++ constexpr Hints
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 为什么std::isnan 不是 constexpr?
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 更多constexpr容器是否需要mark_immutable_if_consexpr
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 从函数角度看ID到文件路径的内部与外部映射
- 通过组合不同的类型来创建唯一的id
- constexpr上下文中std::initializer_list的验证
- constexpr 唯一 ID,使用 clang 编译,但不使用 GCC 编译
- C++ constexpr thread_local id
- 支持相等比较的类的constexpr ID