1

I wrote a generic matrix class like so:

template <std::size_t N, std::size_t M, class T>
class CMatrix
{
protected:
    T* m_elem;
public:
    CMatrix() : m_elem(new T[N*M]) {}
    ~CMatrix() { delete[] m_elem; }
    inline bCMatrix& operator*=(const T& val);
    inline bCMatrix& operator/=(const T& val);
    ...
    template <std::size_t L> inline CMatrix<L, N, T> operator*(const CMatrix<M, L, T>& mat) const;
};

Now I would like to make several specializations for CMatrix<2,2,T>. I'd like to add a comstructor CMatrix<2,2,T>(const T& a00, const T& a01, const T& a10, const T& a11) {...}. And I like to specialize the matrix multiplication for CMatrix<2x2>*CMatrix<2x2>. But on the other hand I'd like to keep the functionalities of *= and /= and so on.

How would I do so?

My only working attempt is to rewrite (copy/past/search/replace) all the code for the specialization N=3, M=3, like:

template <class T>
class CMatrix<3,3,T>
{
...
};

Now I can specialize the matrix-matrix multiplication for 2x2. But I have two nearly identical pieces of code, like one for the generic matrix-vector multiplication and one for the matrix-vector multiplication with matrix 2x2. Which both have to be changed if I optimize the generic one. Sucks to maintain that.

Dominic Hofer
  • 5,751
  • 4
  • 18
  • 23
  • 2
    What are the attempts you've made? – sgvd Mar 10 '16 at 15:47
  • 4
    `template class CMatrix<2, 2, T> {};` – Cornstalks Mar 10 '16 at 15:47
  • First I would like to ask why you are using a pointer with dynamic allocation instead of an array? Since `N` and `M` are know at compile time you can just use `T m_elem[N*M];` – NathanOliver Mar 10 '16 at 15:47
  • 4
    @Nathan or `std::array`! – rubenvb Mar 10 '16 at 15:53
  • 1
    @rubenvb, don't be overzelaous. There is nothing wrong with built-in array in this scenario. – SergeyA Mar 10 '16 at 16:03
  • @sgvd: I thought about adding CMatrix<2,2,T>::CMatrix(const T& a00, const T& a01, const T& a10, const T& a11) {...} but just adding this function outside the class won't help, for it's not defined in the class definition. I thought about a derived class CMatrixNxM from CMatrix to keep the functions. And a class CMatrix2x2 derived from CMatrix<2,2,T> but then the the result of a (2xN)*(Nx2) would be of CMatrixNxM not CMatrix2x2, and won't have access to specializations. – Dominic Hofer Mar 10 '16 at 16:03
  • 2
    @SergeyA: except for the fact that it decays to a pointer, which is why `std::array` was invented. Using `std::array` is by no means "overzealous". – rubenvb Mar 10 '16 at 16:04
  • 1
    `std::array` and normal array will both increase the size of the object itself. If the OP intends this to work for N=M=1000, a stack based object of a megabyte or so is not necessarily a good idea. Using a pointer also allows a very cheap move constructor/assignment. (But for a 2x2, then an array will win hands down.) – Martin Bonner supports Monica Mar 10 '16 at 16:08
  • To the OP. I think what you have to do, is to implement the *= and /= in a base class which is itself templated on the derived class using the "curiously recurring template pattern" (CRTP). – Martin Bonner supports Monica Mar 10 '16 at 16:10
  • 1
    @rubenvb, yes, it decays to pointers **in certain contexts**. So what? – SergeyA Mar 10 '16 at 16:13

1 Answers1

3

You can read some documentation on class template, such as http://en.cppreference.com/w/cpp/language/partial_specialization. For you it entails defining the specialization in a way like this:

template<typename T>
class CMatrix<2, 2, T>
{
public:
    CMatrix<2,2,T>(const T& a00, const T& a01, const T& a10, const T& a11);
    ...
    template <std::size_t L> inline CMatrix<L, 2, T> operator*(const CMatrix<2, L, T>& mat) const;

    // Special overload for 2x2 x 2x2 multiplication
    inline CMatrix<2, 2, T> operator*(const CMatrix<2, 2, T>& mat) const;
};

To prevent duplication of code for operator*= etc you can make a template<int M, int N, typename T> class CMatrixBase {} and implement them in there, and have your actual matrix classes and specializations inherit from that.

Martin suggested to use CRTP, which helps you to not lose your type information. This would look like this:

template<int M, int N, typename T, typename Derived>
class CMatrixBase
{
public:
    Derived& operator*=(T val) {
        doMultiplication(val);
        return *static_cast<Derived*>(this);
    }
};
sgvd
  • 3,819
  • 18
  • 31
  • The CRTP looks promising. So it would boil down to template class CMatrix : public CMatrixBase> {}; But then CMatrixBase only has access to CMatrix, no chance to use template CMatrix. Or am i wrong? – Dominic Hofer Mar 10 '16 at 18:23