协变返回类型无效

时间:2012-11-05 13:10:50

标签: c++ class inheritance

以下是一些代表性的代码,它可以解决我遇到的错误:

class Data
{
};

class Table
{
  virtual std::vector<Data*> getData() = 0;
  virtual void putData(Data* dataItem) = 0;
  virtual Data* getData(int index) = 0;
};

class DerivedData : Data
{
};

class DerivedTable : Table
{
  std::vector<DerivedData*> getData() { return myData; } // invalid covariant return type
  void putData(DerivedData *dataItem) { myData.push_back(dataItem); }
  virtual DerivedData* getData(int index) { return myData[index]; } // invalid covariant return type

  std::vector<DerivedData*> myData;
};

首先,我不太明白为什么putData的覆盖对改变的参数感到满意,但是我不能改变getData的返回类型,虽然我很欣赏这是我可以理解的东西来自更多阅读。

其次,我的主要问题是,如何更改此代码以使其工作。我的基本目标是允许存储和控制数据对象的多个“表”对象。虽然每个数据对象将共享一些共同的东西,但是表格将控制和使用这些差异。例如,一个表可能包含具有name参数的数据对象,因此该表将提供一个函数,该函数打印其所拥有的数据的所有名称的列表。通过这种方式,我可以使用适用于所有这些表对象的通用代码,以及仅使用一种类型的表操作的专用代码。

2 个答案:

答案 0 :(得分:5)

很多事情都错了:

  1. putData的两个版本只是不同的,无关的重载。对于在C ++中重写虚函数,没有“逆变参数类型”这样的东西(即使有,它也会反过来!!)。将关键字override添加到派生函数,以使编译器产生错误。

  2. 类模板不会像您想象的那样工作。如果template <typename T> class Foo是一个类模板,则Foo<X>Foo<Y> 完全不同的,不相关的类,无论X还是Y以任何方式相关。


  3. 正如@Beta所说,您可能只有一个简单的std::vector<std::unique_ptr<Data>>作为主要数据结构。但无论如何,如果你真的必须有一些层次结构,这里有一个可能的“解决方案”:

    #include <memory>
    #include <vector>
    
    struct Data { virtual ~Data() { } };
    
    struct Table
    {
        virtual ~Table() { }
    
        typedef std::unique_ptr<Data> data_ptr;
        typedef std::vector<data_ptr> dataset_type;
    
        virtual dataset_type & getData() = 0;
        virtual void putData(data_ptr dp) = 0;
        virtual Data & getData(std::size_t n) = 0;
    };
    
    class DerivedTable : public Table
    {
        dataset_type myData;
    
    public:
    
        virtual void putData(data_ptr p) override
        {
            myData.push_back(std::move(p));
        }
    
        Data & getData(std::size_t n) override
        {
            return *myData[n];
        }
    
        // ...
    };
    

答案 1 :(得分:1)

&#34;&#34;无效的协变返回类型实际上是由于您尝试更改reserve的返回类型。虽然返回类型不是函数标识符的一部分,但它仍然受到一些限制。 覆盖某些基类的任何方法都应该表现得相似。即虚拟方法实现将尝试向getData()进行向上转换,以便从Data(不是getData())的角度为从Table*调用DerviedTable*的客户端返回有效指针。这可以与(Data*)myData[index]的上下文中的DerviedTable::getData(int)进行比较。

这是编译器混淆的确切位置。

class DerivedData : Data
{
};

此声明称DervidedData私下继承Data 。即DerviedData的任何客户都不知道&#34;它实际上是Data。 这也会导致DerviedData* getData() override的代码无法生成,因为从Data无法访问DeviedTable::getData(int)的{​​{1}}。

要使该错误消失,您可以将继承visbile公开:

class DerivedData : public Data
{
};

或结交朋友

class DerivedData : Data
{
    friend class DerivedTable;
};

虽然这个设计仍然是一个问题的主题。