C++编译期收集模板方法已实例化类型并实现编译期存在性判断
实现方案
C++ 标准没有内置查询模板实例化记录的原生能力,但可以通过**有状态元编程(友元注入+ADL查找)**的技巧实现编译期动态增长的类型列表,完全满足你需要的实例化状态检查需求。
完整可运行代码
#include <cstdio> #include <type_traits> // 基础类型标记结构 template <typename T> struct type_tag { using type = T; }; struct A { template <typename T> void bar(const T&) { // 编译期检查当前类型是否已经被注册过 if constexpr (requires { registered(type_tag<T>{}); }) { // 类型已实例化过的处理逻辑 puts("当前类型已处理"); } else { // 类型首次实例化的处理逻辑,同时注入探针函数完成注册 friend auto registered(type_tag<T>) { return true; } puts("当前类型首次处理"); } } }; // 测试用自定义类型 struct Msg1 {}; struct Msg2 {}; int main() { A a; a.bar(Msg1{}); // 输出:当前类型首次处理 a.bar(Msg1{}); // 输出:当前类型已处理 a.bar(Msg2{}); // 输出:当前类型首次处理 a.bar(Msg2{}); // 输出:当前类型已处理 return 0; }
逻辑说明
- 第一次用某类型
T调用bar时,requires表达式检测不到对应的registered探针函数,进入首次处理分支,通过友元声明将对应T的registered函数注入到外层命名空间,完成类型注册。 - 后续再用同类型
T调用bar时,requires表达式可以直接找到已经注入的探针函数,进入已处理分支。 - 所有注入的探针函数会在编译期留存,相当于你需要的动态增长
typelist,额外增加编译期索引遍历逻辑就可以导出所有已实例化的T组成的完整类型列表。
注意事项
- 该技巧支持C17及以上标准,C20明确了其合法性,主流编译器(GCC、Clang、MSVC)均兼容。
- 单翻译单元下使用无额外问题,跨翻译单元共享实例化状态需要额外做全局声明处理。
内容的提问来源于stack exchange,提问作者Juergen
相关产品推荐
相关产品推荐

