如何无需中间基类,通过基类指针判断派生模板类是否为同一模板特化?
判断模板类特化的无中间基类方案
当然可以不用额外的中间基类来实现这个需求!C++标准本身没有直接提供"模板类型ID"的机制,但我们可以通过多态虚函数或者CRTP模式来实现类似的效果,下面是两种实用的方案:
方案1:虚函数标记法
给基类A添加一个虚函数,让每个模板类的所有特化都返回同一个唯一标记(比如用某个固定特化的type_info),这样就能通过基类指针判断对象是否属于该模板的任意特化。
#include <typeinfo> #include <vector> #include <iostream> class A { public: virtual ~A() = default; // 必须有虚函数才能支持多态 // 默认返回nullptr,表示不属于任何模板特化 virtual const std::type_info* get_template_signature() const { return nullptr; } }; template<typename T> class B : public A { public: // 所有B的特化共享同一个静态标记:用B<void>的type_info作为模板的唯一标识 static const std::type_info* template_signature() { return &typeid(B<void>); } // 重写基类虚函数,返回模板的标记 const std::type_info* get_template_signature() const override { return template_signature(); } }; template<typename T> class C : public A { public: static const std::type_info* template_signature() { return &typeid(C<void>); } const std::type_info* get_template_signature() const override { return template_signature(); } }; int main() { std::vector<A*> v = {new B<int>(), new C<double>(), new B<std::string>()}; for (auto* ptr : v) { // 对比标记,判断是否为B的任意特化 if (ptr->get_template_signature() == B<int>::template_signature()) { std::cout << "Detected a specialization of B\n"; } } // 清理内存 for (auto* ptr : v) delete ptr; return 0; }
关键说明:
- 我们用
B<void>的type_info作为所有B特化的统一标记——因为不管T是什么,B<T>的静态函数template_signature()都会返回同一个指针。 - 如果你的模板类不能接受
void作为参数(比如需要T是可构造类型),可以换成一个通用的合法类型(比如int)。
方案2:CRTP简化版(推荐)
如果有多个模板类需要实现这个判断,重复写虚函数会很繁琐,这时候可以用**奇异递归模板模式(CRTP)**来封装逻辑,避免重复代码:
#include <typeinfo> #include <vector> #include <iostream> class A { public: virtual ~A() = default; // 通用判断接口:传入目标模板的标记,返回是否匹配 virtual bool belongs_to_template(const std::type_info& target_sig) const { return false; } }; // CRTP基类,封装模板标记逻辑 template<template<typename> class Template> class TemplateIdentifier : public A { public: bool belongs_to_template(const std::type_info& target_sig) const override { // 用Template<void>的type_info作为当前模板的标记 return target_sig == typeid(Template<void>); } }; // 模板类只需继承CRTP基类,无需自己写虚函数 template<typename T> class B : public TemplateIdentifier<B> {}; template<typename T> class C : public TemplateIdentifier<C> {}; // 封装一个获取模板标记的辅助函数,更接近你想要的语法 template<template<typename> class Template> const std::type_info& get_template_id() { return typeid(Template<void>); } int main() { std::vector<A*> v = {new B<int>(), new C<double>(), new B<std::string>()}; for (auto* ptr : v) { // 调用辅助函数获取B的标记,传入判断接口 if (ptr->belongs_to_template(get_template_id<B>())) { std::cout << "Detected a specialization of B\n"; } } for (auto* ptr : v) delete ptr; return 0; }
优势:
- 所有模板类只需要一行继承代码,无需重复实现虚函数,代码更简洁易维护。
- 辅助函数
get_template_id<Template>让调用语法更接近你理想中的templateTypeId(B*)形式。
注意事项
- 必须保证基类有虚函数:只有存在虚函数时,基类指针才能正确调用派生类的重写方法,
typeid和动态多态才能正常工作。 - 跨动态链接库的情况:如果你的代码涉及动态链接库,同一个类型的
type_info可能会有多个实例,这时候可以改用比较type_info.name()(注意:name()的格式是编译器相关的,但同一个模板的特化标记的name()是一致的)。 - 多参数模板的适配:如果你的模板是多参数的(比如
template<typename T, typename U> class D),只需调整CRTP基类为接受多参数的模板模板参数即可,标记用typeid(D<void, void>)(确保模板支持该参数组合)。
对比你原来的中间基类方案,这两种方法不需要额外的无意义基类,直接利用C++的多态和模板特性实现需求,代码更干净。
内容的提问来源于stack exchange,提问作者Morten B.
相关产品推荐
相关产品推荐

