Reputation: 311
I'm trying to make an interface called Matrix. Two classes will implement this interface. One of them is called RegMatrix (Regular Matrix). Now, I'm trying to build iterators for the classes RegMatrix and OthMatrix, and let the user to be able to iterate over a 'Matrix' object. The problem is that i'm getting an error "invalid covariant return type for 'virtual RegMatrix::iterator RegMatrix::begin()'" for the methods begin() and end(), probably because one of them returns a RegMatrix::iterator , and the base one returns Matrix::iterator. I don't understand what is actually wrong in this. Does anybody know how to fix this issue ? Thanks.
Edit: i understand from your current answers my design is invalid. So, can anybody suggest a better design/solution for my problem? iterating over a 'Matrix' that could be either an insance of 'RegMatrix' (implemented with a Map to hold data) or an instance of a 'OthMatrix' (implemented with a Vector). those two has different iterators and i want a Wrapper iterator to wrap these two so the kind of instance will be transparent to the user when iterating. thanks.
Class Matrix (The interface) :
class Matrix
{
public:
class iterator
{
public:
virtual iterator& operator=(const iterator &other);
virtual ~iterator(){}
double operator*() const;
bool operator==(const iterator &other) const;
bool operator!=(const iterator &other) const;
iterator &operator++();
iterator &operator++(int);
};
virtual iterator begin() = 0; //*** ERROR : overriding 'virtual Matrix::iterator Matrix::begin()' ***
virtual iterator end() = 0; //*** ERROR : overriding 'virtual Matrix::iterator Matrix::end()' ***
};
Class Regular matrix :
class RegMatrix : public Matrix
{
public:
RegMatrix() {//TODO };
class iterator : public Matrix::iterator{
friend class RegMatrix;
public:
iterator& operator=(const iterator &other) {
//TODO
}
~iterator() {}
double operator*() const {
//TODO
}
bool operator==(const iterator &other) const {
//TODO
}
bool operator!=(const iterator &other) const {
//TODO
}
iterator &operator++() {
//TODO
}
iterator &operator++(int)
{
//TODO
}
iterator(Vector2D::iterator place)
{
rowIter = place;
}
private:
Vector2D::iterator rowIter;
Vector::iterator colIter;
};
iterator begin() { //*** ERROR : invalid covariant return type for 'virtual RegMatrix::iterator RegMatrix::begin()' *** //
return iterator(matrix.begin());
}
iterator end() { //*** ERROR : invalid covariant return type for 'virtual RegMatrix::iterator RegMatrix::end()' *** //
return iterator(matrix.end());
}
private:
Vector2D matrix;
};
Upvotes: 0
Views: 435
Reputation: 2151
Your code is invalid, because you are trying to override Matrix::iterator Matrix::begin()
with RegMatrix::iterator RegMatrix::begin()
; the return types are different. This is only allowed if the return types are "covariant":
§ 10.3.7
The return type of an overriding function shall be either identical to the return type of the overridden function or covariant with the classes of the functions. If a function D::f overrides a function B::f, the return types of the functions are covariant if they satisfy the following criteria:
- both are pointers to classes, both are lvalue references to classes, or both are rvalue references to classes
- the class in the return type of B::f is the same class as the class in the return type of D::f, or is an unambiguous and accessible direct or indirect base class of the class in the return type of D::f
- both pointers or references have the same cv-qualification and the class type in the return type of D::f has the same cv-qualification as or less cv-qualification than the class type in the return type of B::f.
Because your return types are neither pointers nor references, they cannot be covariant.
You could use the Pimpl idiom to fix your design; it allows having one single iterator type that is able to iterate across different container types by injecting a concrete implementation.
Here is a stripped down example to show the basic concept:
#include <memory>
class Matrix
{
public:
class iterator
{
public:
// this is the interface for the implementation the iterator is using
struct impl
{
virtual ~impl() {}
virtual double dereference() const = 0;
// [...]
};
// the constructor takes a implementation
iterator(std::unique_ptr<impl> impl)
: m_impl{std::move(impl)}
{
}
double operator*() const
{
// all calls are referred to the concrete implementation
return m_impl->dereference();
}
// [...]
private:
std::unique_ptr<impl> m_impl;
};
virtual iterator begin() = 0;
virtual iterator end() = 0;
};
class RegMatrix : public Matrix
{
// every Matrix has its own iterator implementation
class iterator_impl : public Matrix::iterator::impl
{
public:
iterator_impl(Vector2D::iterator itr)
: m_itr{itr}
{
}
virtual double dereference() const override
{
return *m_itr;
}
// [...]
private:
Vector2D::iterator m_itr;
};
virtual iterator begin() override
{
// return a iterator that holds our iterator implementation
return iterator(std::unique_ptr<iterator_impl>{new iterator_impl{matrix.begin()}});
}
virtual iterator end() override
{
return iterator(std::unique_ptr<iterator_impl>{new iterator_impl{matrix.end()}});
}
private:
Vector2D matrix;
};
This should be able to solve your problem, but it also has drawbacks: Every iterator construction induces a heap allocation, and every iterator operation leads to a virtual method call. This could become a performance issue in critical situations, especially because iterators are intended to be lightweight objects.
Upvotes: 1
Reputation: 52471
Covariant return types are only allowed when returning a pointer or a reference (to a base and derived class correspondingly) - not when returning by value.
Think about it - the caller needs to know how much memory to reserve for storing the return value. But sizeof(RegMatrix::iterator) > sizeof(Matrix::iterator)
. How is the caller that only has Matrix*
pointer in hand supposed to know which of them begin()
call will return?
Upvotes: 0