Invalid covariant return type
Solution 1
Many things are wrong:
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 keywordoverride
to the derived function to make your compiler produce an error.Class templates don't work the way you think. If
template <typename T> class Foo
is a class template, thenFoo<X>
andFoo<Y>
are totally different, unlrelated classes, no matter whetherX
andY
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.
Jonathan Pierce
Updated on June 04, 2022Comments
-
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.