Invalid covariant return type

10,663

Solution 1

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

    // ...
};

Solution 2

"invalid covariant return type for" actually caused by the fact that you try to change the return type of getData(). Though return type isn't part of function identifier it still subject for some restriction. Any method that overrides some base class should behave similary. I.e. virtual method implementation will try to do upcast to Data to return a valid pointer for clients that calls getData() from perspective of Table* (not DerviedTable*). This may be compared to (Data*)myData[index] from context of DerviedTable::getData(int).

That's the exact palce where compiler gets its confusion from.

class DerivedData : Data
{
};

This declaration says that DervidedData inherits Data privately. I.e. any client of DerviedData doesn't "know" that it in fact it is a Data. That also makes generation of code for DerviedData* getData() override impossible since upcast to Data is inaccessible from DeviedTable::getData(int).

To make that error go away you can either make inheritance visbile to public:

class DerivedData : public Data
{
};

Or make a friend

class DerivedData : Data
{
    friend class DerivedTable;
};

Though this design still a subject for questions.

Share:
10,663
Jonathan Pierce
Author by

Jonathan Pierce

Updated on June 04, 2022

Comments

  • Jonathan Pierce
    Jonathan Pierce almost 2 years

    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.