如何使C++模板类实例保留原类的继承关系?
你遇到的这个问题本质是C++模板的特性导致的:模板实例化生成的foo<base>和foo<derived>是完全独立的类型——哪怕base和derived是父子类,它们对应的模板类之间也不会自动建立继承关系。要实现foo_base*可以像base*指向derived那样指向foo_derived对象,我们需要手动给模板类复刻原类的继承层次。
下面提供两种可行的实现方案:
方案一:手动声明基类类型,构建模板继承链
这种方法需要给每个类显式标记它的基类,然后让模板类根据这个标记自动继承对应的foo实例:
首先修改原有的base和derived,添加基类型别名,并使用虚拟继承避免后续多重继承的二义性:
#include <iostream> #include <type_traits> struct base { // 标记自身没有上层基类 using base_type = void; virtual int test() { return 0; } }; struct derived : virtual public base { using base_type = base; virtual int test() override { return 1; } };
然后定义foo模板,通过条件继承构建和原类对应的继承链:
// 前置声明foo模板 template <typename T> struct foo; // 特化base的情况:只继承base template <> struct foo<base> : virtual public base { virtual int bar() { return 2; } }; // 通用模板:继承当前类T,同时继承对应基类的foo实例 template <typename T> struct foo : virtual public T, public foo<typename T::base_type> { // 显式覆盖test(),明确调用T的实现以避免二义性 virtual int test() override { return T::test(); } virtual int bar() override { return 2; } };
现在测试代码就能正常运行了:
typedef foo<base> foo_base; typedef foo<derived> foo_derived; int main(int argc, char ** argv) { base * p = new derived(); std::cout << p->test() << std::endl; // 输出1 foo_base * foo_p = new foo_derived(); std::cout << foo_p->test() << std::endl; // 输出1 std::cout << foo_p->bar() << std::endl; // 输出2 return 0; }
方案二:自动检测基类(更通用)
如果不想手动给每个类添加base_type,可以用模板元编程自动检测类的继承关系,动态构建模板的继承链:
#include <iostream> #include <type_traits> struct base { virtual int test() { return 0; } }; struct derived : virtual public base { virtual int test() override { return 1; } }; // 辅助模板:检测T是否继承自base template <typename T> struct is_derived_from_base : std::is_base_of<base, T> {}; // 前置声明foo模板,带一个布尔参数标记是否继承自base template <typename T, bool = is_derived_from_base<T>::value> struct foo; // 特化:当T就是base本身时 template <> struct foo<base, false> : virtual public base { virtual int bar() { return 2; } }; // 特化:当T继承自base时,自动继承foo<base> template <typename T> struct foo<T, true> : virtual public T, public foo<base> { virtual int test() override { return T::test(); } virtual int bar() override { return 2; } };
这个方案不需要修改原有的base和derived类,只要是继承自base的类型,对应的foo<T>都会自动成为foo<base>的子类,实现类型兼容。
关键注意点
- 虚拟继承的作用:因为
foo<derived>会同时通过foo<base>和derived继承base,使用虚拟继承可以避免base被多次实例化,解决成员访问的二义性问题。 - 显式覆盖
test():多重继承可能导致test()函数的调用歧义,所以需要在foo<T>中显式覆盖该函数,并指定调用T的实现。 - 模板继承链的意义:通过让
foo<Derived>继承foo<Base>,我们复刻了原类的继承关系,使得foo<Base>*可以安全地指向foo<Derived>对象,完全模拟了原类的多态行为。
内容的提问来源于stack exchange,提问作者whitebob
相关产品推荐
相关产品推荐

