如何使C++模板化抽象基类与模板参数实现协变?
解决方法
方案1:编写适配类包装Reader
Reader<Derived>和Reader<Base>本身无继承关系,但Derived是Base的子类,我们可以写一个适配类,继承Reader<Base>,内部持有Reader<Derived>的引用,实现Get()方法时返回Derived对象的引用(会自动隐式转换为Base引用)。
代码示例:
template <typename DerivedT, typename BaseT> class ReaderAdapter : public Reader<BaseT> { public: explicit ReaderAdapter(const Reader<DerivedT>& reader) : reader_(reader) {} const BaseT& Get() const override { // DerivedT是BaseT的子类,const DerivedT&可隐式转为const BaseT& return reader_.Get(); } private: const Reader<DerivedT>& reader_; };
使用方式:
void Run() { std::unique_ptr<Reader<Derived>> reader = GetReaderSomehow(); ReaderAdapter<Derived, Base> adapter(*reader); Frobnicate(adapter); }
方案2:将Frobnicate改为模板函数
如果不需要严格的多态逻辑(比如Frobnicate只需要处理能返回可转为Base类型的Reader),可以把Frobnicate改成模板函数,直接接受任意Reader<T>,只要T能隐式转换为Base。
代码示例:
template <typename T> void Frobnicate(const Reader<T>& reader) { const Base& value = reader.Get(); // 后续业务逻辑... }
修改后Run函数里直接调用Frobnicate(*reader)即可正常编译。
方案3:重构Reader模板,建立协变继承关系
如果希望从根本上让Reader<Derived>能作为Reader<Base>使用,可以重构Reader模板,利用SFINAE控制继承关系,让Reader<T>在T是Base子类时自动继承Reader<Base>:
#include <type_traits> // 前置声明基础模板 template <typename T, typename = void> class Reader; // 非Base子类的基础定义 template <typename T> class Reader<T, std::enable_if_t<!std::is_base_of_v<Base, T>>> { public: virtual const T& Get() const = 0; }; // 当T是Base子类时,继承Reader<Base> template <typename T> class Reader<T, std::enable_if_t<std::is_base_of_v<Base, T>>> : public Reader<Base> { public: const Base& Get() const override { return GetDerived(); } // 子类需要实现的专属方法 virtual const T& GetDerived() const = 0; };
此时Reader<Derived>会自动继承Reader<Base>,Run函数里直接Frobnicate(*reader)就能正常运行。不过这种方式需要修改原有Reader接口,需根据实际场景评估可行性。
内容的提问来源于stack exchange,提问作者gct
相关产品推荐
相关产品推荐

