Nyquist
Nyquist

Reputation: 336

Return covariant type of specialized template class

I have the following class hierarchy:

template<typename T>
class GridMetric{
  virtual GridMetric* getNeighbors(T value) = 0;
};

template<size_t N, typename T, typename Derived>
class MatrixBase : public GridMetric<T>{
  virtual MatrixBase<N,T,Derived>* getNeighbors(T value){return nullptr;}
};

template<size_t N, typename T>
class MatrixND : public MatrixBase<N,T,MatrixND<N,T>>{
  virtual MatrixND<2,T>* getNeighbors(T value){ /* ... */}
};

template<typename T>
class MatrixND<2,T> : public MatrixBase<2,T,MatrixND<2,T>>{
  virtual MatrixND<2,T>* getNeighbors(T value){ /* ... */}
};

template<typename T>
class Vector : public GridMetric<T>{
  virtual MatrixND<2,T>* getNeighbors(T value){ /* ... */}
};

So my abstract class GridMetric has two derived classes, Vector and MatrixBase. My Matrix Base class has crtp style derived class MatrixND and there is a specialization of MatrixND with N=2.

Each class shall have a virtual function getNeighbors to return a MatrixND<2,T> pointer.

It all works fine, except the MatrixND class complains that MatrixND<2,T> is an invalid covariant return type:

error: invalid covariant return type for ‘MatrixND<2ul, T>* MatrixND<N, T>::getNeighbors(T&) [with long unsigned int N = 3ul; T = double]’
  virtual MatrixND<2,T>* getNeighbors(T& in){

My first question is why and how can I deal with that? Since MatrixND<2,T> inherits from MatrixBase!

My second question: Is it bad design, because I would always return raw pointers? I read the expression return new obj.. a lot, but also that this is apparently bad design. Are there other possibilities to achieve the same?

Edit: So, after some time, I realized, that the original plan wasn't going to work out and I found a much easier solution by templating the class, where I want to use those classes.

Anyway, the question stands, why the specialized MatrixND class can not be a covarient return type in the general MatrixND class. I didn't find anything saying it isn't allowed.

Upvotes: 0

Views: 103

Answers (1)

O&#39;Neil
O&#39;Neil

Reputation: 3849

  • MatrixND<2,T>, basic case, no need for an explanation.
  • Vector<T> inherits from GridMetric<T>, so anything deriving from GridMetric<T> (including MatrixND<2,T> here) is fine.

But, the general form MatrixND<N,T> (N != 2) inherits from MatrixBase<N,T,MatrixND<N,T>> which redefines the virtual function as:

virtual MatrixBase<N,T,Derived>* getNeighbors(T value){return nullptr;}

which forces the return type of MatrixND<N,T> to now derive (at least) from MatrixBase<N,T,MatrixND<N,T>>.
Not from GridMetric<T> (as for Vector<T>) or MatrixBase<2,T,MatrixND<2,T>> (as for the specialization MatrixND<2,T>), but: MatrixBase<N,T,MatrixND<N,T>>, with (N != 2).

And why this is not a covariant return type? Because MatrixND<2,T> does not inherit from MatrixBase<N,T,MatrixND<N,T>> when N != 2.

Either remove the function redefinition within MatrixBase<N,T,MatrixND<N,T>> or change its return type to GridMetric<T>, and this will work (as MatrixND<2,T> inherits from GridMetric<T>).

Upvotes: 1

Related Questions