我无法覆盖基类的方法,因为我的派生类是模板化的

Posted

技术标签:

【中文标题】我无法覆盖基类的方法,因为我的派生类是模板化的【英文标题】:I can't override Base class's method because my derived class is templatised 【发布时间】:2017-08-25 11:57:42 【问题描述】:

我尝试重写基类函数,但是因为我已经模板化了它的子类/派生类/子类,所以我不能根据实例化模板的类型来重写该函数。

struct BaseType

    virtual double getPropertyValue() = 0; 
;

template <typename T>
struct NumberType : BaseType

    T propertyValue;
    T getPropertyValue() override  return propertyValue;   // I would like this to return whatever T is.
;

int main()

    std::vector<BaseType*> vectr;
    NumberType<double> doubleType;  // This is fine, overrides the base function
    NumberType<int> intType;    // Has no overrider, won't compile

所以我想也许我也可以模板化基类,以便基类函数也返回 T。但如果我这样做,我将无法将它们保存在容器中或指向它们,因为它们都属于不同的 Base 类型。

我还考虑过模板化 Base 并让它从更高的父级(未模板化)继承,但我遇到了同样的问题。

有没有办法解决这个问题?

【问题讨论】:

"我不能重写基类的方法,因为我的派生类是模板化的" 这不是你不能重写的原因。 对不起,我知道这听起来如何,你是对的。不仅仅是因为我已经将它模板化了,还因为我有依赖于模板参数的返回类型。 为什么你需要重写一个纯虚函数?在我看来,您正在混合静态和动态多态性。 为什么要将不同的类型放入同一个容器中?由于类型差异,您将无法正确使用它们。您实际上可以使用template &lt;class T&gt; std::vector&lt;NumberType&lt;T&gt;&gt; numbers; 来避免丢失类型信息。 我正在尝试抽象出不同的数据类型,这样我就可以拥有一个不同类型的容器,我认为他们称之为类型擦除?这是为了创建我自己的脚本,您可以在运行时动态创建自己的类,但看起来这是不可能的。 【参考方案1】:

如果返回值T 与纯虚函数的返回值协变,您可以执行此操作。但遗憾的是,T 通常不会与 double 协变。

接受你正在混合静态和动态多态性技术,这可能是一个设计缺陷,你可以定义

struct Cov ;

struct BaseType

    virtual Cov& getPropertyValue() = 0; 
;

那么,

template <typename T>
struct NumberType : BaseType

    T propertyValue;
    T& getPropertyValue() override  return propertyValue; 
;

其中TCov 的子类:这种关系意味着T&amp; 是与Cov&amp; 协变的返回类型,因此编译将成功。这样做还可以避免获取T 的值副本。您可能还会发现为各种 Ts 构建 conversion 运算符很方便,这些运算符最终会被构建为具有原始类型返回值。

您还可以引入const 引用返回类型以满足确切的要求。

【讨论】:

从答案中的示例代码来看,intdouble 都不是 Cov 的子类。如果没有答案中未解释的额外机器,它将无法解决问题。 这非常非常有趣。协变,这是 Visual Studio 使用的词,表示它必须是协变的。这看起来很有趣,我去看看我能做什么。 @skypjack 是的,这是我想到的第一件事,然后我想我只是将原始数据类型包装在类中。哇,有无穷无尽的解决方案。好吧,这可能是个坏主意,但我仍然喜欢看到这有多疯狂。 如果您确实在类中包装了原始类型,那么您还可以在这些类中定义 conversion 运算符来生成原始类型! @Zebrafish 好吧,如果您可以随意更改 everything 的定义,那么可以:有很多不同的解决方案。【参考方案2】:

BaseType 有一个对其所有后代都具有约束力的合同。它说getPropertyValue 返回doubleNumberType 不能修改合同,它是一成不变的。

让我们假装合同不存在。

BaseType& base = BaseContainer.getSome();

// base can be NumberType<complex> or 
// or NumberType<tensor<double,4,4,4>> or NumberType<padic<5>>
// or a type that is not even thought of yet 
// as of now.

 what = base.getPropertyValue(); // how should 'what' be declared?

如果我们不知道base.getPropertyValue() 的结果是什么,就无法使用它。

使返回类型协变并没有真正的帮助。它只是将问题从BaseType 转移到BaseType::getPropertyValue() 返回的任何基类。

你需要想出一个BaseType的可用接口,并在所有后代类中坚持下去。

【讨论】:

以上是关于我无法覆盖基类的方法,因为我的派生类是模板化的的主要内容,如果未能解决你的问题,请参考以下文章

如果该属性在派生类中被覆盖,如何调用基类的属性?

如何在没有虚方法的情况下创建派生类的基类?

C++:友元函数,派生类

抽象派生类中的抽象方法覆盖/实现抽象基类的抽象或具体方法。如何以及为啥?

派生类无法访问基类的受保护方法

关于C++基类与派生类