Question

I am writing matrix classes. Take a look at this definition:

template <typename T, unsigned int dimension_x, unsigned int dimension_y> 
class generic_matrix
{
  ...
  generic_matrix<T, dimension_x - 1, dimension_y - 1> 
  minor(unsigned int x, unsigned int y) const
  { ... }
  ...
}

template <typename T, unsigned int dimension> 
class generic_square_matrix : public generic_matrix<T, dimension, dimension>
{
  ...
  generic_square_matrix(const generic_matrix<T, dimension, dimension>& other)
  { ... }
  ...
  void foo();
}

The generic_square_matrix class provides additional functions like matrix multiplication. Doing this is no problem:

generic_square_matrix<T, 4> m = generic_matrix<T, 4, 4>();

It is possible to assign any square matrix to M, even though the type is not generic_square_matrix, due to the constructor. This is possible because the data does not change across children, only the supported functions. This is also possible:

generic_square_matrix<T, 4> m = generic_square_matrix<T, 5>().minor(1,1);

Same conversion applies here. But now comes the problem:

generic_square_matrix<T, 4>().minor(1,1).foo(); //problem, foo is not in generic_matrix<T, 3, 3>

To solve this I would like generic_square_matrix::minor to return a generic_square_matrix instead of a generic_matrix. The only possible way to do this, I think is to use template specialisation. But since a specialisation is basically treated like a separate class, I have to redefine all functions. I cannot call the function of the non-specialised class as you would do with a derived class, so I have to copy the entire function. This is not a very nice generic-programming solution, and a lot of work.

C++ almost has a solution for my problem: a virtual function of a derived class, can return a pointer or reference to a different class than the base class returns, if this class is derived from the class that the base class returns. generic_square_matrix is derived from generic_matrix, but the function does not return a pointer nor reference, so this doesn't apply here.

Is there a solution to this problem (possibly involving an entirely other structure; my only requirements are that the dimensions are a template parameter and that square matrices can have additional functionality).

Thanks in advance,

Ruud

Was it helpful?

Solution

You could just implement the function in the derived class without making it virtual. This will "hide" the base class implementation and may be desirable for your use despite the general aversion to hiding member functions.

A better method might be to just use a different name, although that may upset the "purity" of your interface.

Finally- could minor() be implemented as a free function instead of a member function? Then you could provide overloads as appropriate, and the correct function would be called at compile time. This is the closest to the method hiding case I opened with, but I suspect it would be more generally accepted as "good practice."

OTHER TIPS

There is no need for complicating the design if you can simply make minor() a free function:

template<typename T, unsigned int X, unsigned int Y> 
generic_matrix<T, X-1, Y-1> 
minor(const generic_matrix<T, X, Y>& m, unsigned int x, unsigned int y);

... then you can add alternative versions as much as you want.

If you don't use these types polymorphically, you could just override the base class method in the derived class non-virtually so that it returns a derived class object. It would hide the base class version, which usually isn't wanted, but in your case it seems it is.

Or you make these member functions free function templates, if necessary with overloads for different types of matrices. (Although templatization might take care of that automatically.)

Template specialization will work. What you do is take the shared code and put it in a base class and then just inherit from both. Protect the destructor in the base so that nobody can break your concept by casting to the base and trying to delete it.

Something like so:

namespace detail_
{
  template < typename T >
  struct my_templates_common_functionality
  {
    void f1() {}
    void f2() {}
  protected:
    ~my_templates_common_functionality() {}
  };
}

template < typename T >
struct my_template : detail_::my_templates_common_functionality<T>
{
  int f3() { return 5 }
};

template < >
struct my_template<double> : detail_::my_templates_common_functionality<double>
{
  char* f3() { return nullptr; }
};

There are other ways, but that's one of the easiest.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top