views:

172

answers:

4

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

+3  A: 

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."

dash-tom-bang
Thanks very much, hiding does the trick. It's a nice, simple and elegant solution (at least in this case).Using a different name would not be nice as you have to call different members on different classes derived from each other, that perform basically the same thing.minor() _could_ be implemented as a free function, but that would not be a nice OOP solution, the minor is a property of a matrix.I agree that function hiding might not be "good practice", but in this case I think it is, because the function implementation itself does not change. (And it allows for a much nicer matrix class.)
Ruud v A
@Ruud: One could argue that free functions belong to the interface of an object, see e.g. ["How Non-Member Functions Improve Encapsulation"](http://www.drdobbs.com/184401197).
Georg Fritzsche
@Ruud: OO is a means to an end ans not the solution to all problems. C++ is a __multi-paradigm__ language and shines brightest where paradigms are mixed. The STL is a wonderful example for that. Look how powerful it is - with no inheritance at all, and most algorithms being free functions, instead of container members.
sbi
@Georg Fritzsche: You are right here, I think it is mostly a matter of taste. But in my case there is something more: minor() can only be a free function because generic_matrix has functions that expose it's data. As a member, minor() would have access to this data directly, while as a free function, it would need to use the interface. I suppose compilers will optimize this away and there is no difference in practice, but as a member it _is_ easier to implement (though this is a weak argument).@sbi: _C++_ might be multi-paradigm, but _I_ prefer OOP.
Ruud v A
@Ruud - consider that by using 'hiding' the client could always upcast and get the original. If you want to block that then this isn't good enough. As to free functions lacking OO I definitely have to disagree there. Does << lack "OO"? If you're going to improve in C++ you'll need to break out of that paradigm.
Noah Roberts
@Noah Roberts: Upcasting is fine, as the result of the function is the same.<< is an operator. You can't see the difference between a member operator and a free operator when using it: in A<<B, << could be a member function of A, or a free function accepting A and B. When using a function like F,A.F(B) looks clearer—in my opinion—than F(A, B). You can clearly see that something is happening to A based on B (though A could be left unchanged and B could change but that is bad practice in my opinion).But let's not start a flame war about what is better here. It's just a matter of taste.
Ruud v A
@Rudd - see Scott Meyers on the subject: http://www.drdobbs.com/184401197;jsessionid=VAKSR5JFHVZ5JQE1GHPSKHWATMY32JVN
Noah Roberts
A: 

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.

Noah Roberts
+3  A: 

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.

Georg Fritzsche
In general, free functions are the way to go. It's just that people read some much bad advices about Object Oriented design (especially since Java/C# don't have free functions) that they shy from them. And then they discover generic programming and their (bad) OOP reflexes don't fit :/
Matthieu M.
@Matthieu: I wish I could give that comment a +10.
sbi
+1  A: 

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.)

sbi