双重继承类型推导问题:模板类继承后Bar函数调用歧义解决及选型
解决多重模板继承的函数歧义问题及继承vs组合的选择
一、消除Bar函数调用歧义的其他方法
方法1:使用using声明引入基类函数,形成重载
在AwesomeClass中通过using把两个基类的Bar函数引入到当前类作用域,由于两个Bar的参数类型不同(ISomeInterface*和IOtherInterface*),编译器会将它们视为重载函数,调用时自动根据参数类型匹配:
class AwesomeClass : public Foo<ISomeInterface>, public Foo<IOtherInterface> { public: using Foo<ISomeInterface>::Bar; using Foo<IOtherInterface>::Bar; };
之后直接调用a.Bar(i)就不会有歧义了,编译器会根据i的类型自动选择对应基类的版本。
方法2:在AwesomeClass中添加转发函数
手动在AwesomeClass中定义重载的Bar函数,分别转发到对应基类的Bar实现:
class AwesomeClass : public Foo<ISomeInterface>, public Foo<IOtherInterface> { public: void Bar(ISomeInterface* t) { Foo<ISomeInterface>::Bar(t); } void Bar(IOtherInterface* t) { Foo<IOtherInterface>::Bar(t); } };
这种方式更显式,如果你需要在转发时添加额外逻辑(比如参数校验),会比using更灵活。
二、继承vs组合的取舍判断
什么时候适合用继承?
如果你的AwesomeClass需要被当作Foo<ISomeInterface>或Foo<IOtherInterface>类型使用——比如要传递给接受Foo<ISomeInterface>&的函数,或者需要满足某个基于Foo<T>的接口要求,那继承是合理的选择,因为它让AwesomeClass具备了基类的接口和类型特性。
什么时候更适合用组合?
如果AwesomeClass只是需要内部持有这两个Foo实例的功能,不需要被当作Foo<T>类型暴露,那组合更优,原因如下:
- 降低耦合:组合关系中,
AwesomeClass只依赖Foo的公开接口,不会受Foo内部实现或非公开成员变化的影响; - 避免多重继承潜在问题:虽然这里是模板实例化的多重继承,没有菱形继承的风险,但组合在设计上更灵活,后续修改
AwesomeClass的功能时,调整内部成员比修改继承关系更简单; - 符合“组合优于继承”的设计原则:继承是“is-a”关系,组合是“has-a”关系,如果你只是需要
AwesomeClass拥有两个Foo的功能,而非“是”Foo的一种,组合更贴合设计意图。
组合的示例实现:
class AwesomeClass { private: Foo<ISomeInterface> m_someFoo; Foo<IOtherInterface> m_otherFoo; public: void Bar(ISomeInterface* t) { m_someFoo.Bar(t); } void Bar(IOtherInterface* t) { m_otherFoo.Bar(t); } // 若需要暴露Foo的其他接口,可按需添加转发函数 };
内容的提问来源于stack exchange,提问作者Frank
相关产品推荐
相关产品推荐

