Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid covariant return type

Here is some representative code that gets the error I'm experiencing:

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;
};

Firstly, I don't quite understand why it is that the override of putData is happy with the parameters being changed, but I can't change the return type for getData, although I appreciate this is something I can gain an understanding of from more reading.

Secondly, and my main question, how could this code be changed to make it work. My basic goal is to allow for multiple "table" like objects which will store and control data objects. While each data object will share some things in common, there will be some distinct differences which the table will control and work with. For example, one table might have data objects which have a name parameter, and so the table will provide a function which prints a list of all the names of the data it holds. This way I can have generic code which works with all of these table objects, and specialized code which operates only with one type of table.

like image 213
Jonathan Pierce Avatar asked Jan 28 '26 09:01

Jonathan Pierce


1 Answers

Many things are wrong:

  1. The two versions of putData are simply different, unrelated overloads. There is no such thing as a "contravariant argument type" for overriding virtual functions in C++ (and even if there were, it would go the other way round!!). Add the keyword override to the derived function to make your compiler produce an error.

  2. Class templates don't work the way you think. If template <typename T> class Foo is a class template, then Foo<X> and Foo<Y> are totally different, unlrelated classes, no matter whether X and Y are related in any way.


As @Beta says, you might just have a simple std::vector<std::unique_ptr<Data>> as your main data structure. But anyway, if you really must have some hierarchy, here's a possible "solution":

#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];
    }

    // ...
};
like image 153
Kerrek SB Avatar answered Jan 29 '26 21:01

Kerrek SB