3

I am trying to define a Vector as a row/column Matrix. Vector needs some methods which Matrix doesn't have, so I specialized Matrix:

template<typename T, unsigned N, unsigned M>
struct Matrix {
    T data[N][M];
};

template <typename T, unsigned N>
struct Matrix<T, N, 1> : public Matrix<T, N, 1> {
    T at(unsigned index) {
        return data[index][0];
    }
};

template <typename T, unsigned N>
using Vector = Matrix<T, N, 1>;

This code does not compile, because the specialization is a recursive type. The reason I want inheritance here is so that I can include all of the content of Matrix into the specialization without copy&paste.

Is there a way that I can instantiate the original Matrix and inherit from it? And if there was, would my type become non-recursive?

Another way of solving this problem that comes to mind is to simply #include the contents of a regular Matrix into both the initial definition and all specializations. But this is far from idiomatic.

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96

3 Answers3

2

Here is one way to do it:

template<typename T, unsigned N, unsigned M>
struct Matrix {
    T data[N][M];
};

template<typename T, unsigned N>
struct MatrixTN : Matrix <T, N, 1> {
    T at(unsigned index) {
        return this->data[index][0];
    }
};

template<typename T, unsigned N>
using Vector = MatrixTN <T, N>;

Note that this->data is needed to delay evaluation of data to the second phase of template lookup.

Live demo

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
2

In C++20, you might use requires to discard methods:

template<typename T, unsigned N, unsigned M>
struct Matrix {
    T data[N][M];

    T at(unsigned index) requires (M == 1) {
        return data[index][0];
    }
};

In previous standard, you might use SFINAE instead.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

You can use SFINAE to disable method. It's necessary to have template method with template argument dependent on the class template parameter.

C++11:

#include <type_traits>

template<typename T, unsigned N, unsigned M=1>
struct Matrix 
{
    T data[N][M];

    template<typename V = T>
    typename std::enable_if<M == 1, V>::type at(unsigned index)
    {
        return data[index][0];
    }
};

template <typename T, unsigned N>
using Vector = Matrix<T, N, 1>;

int main()
{
    Matrix<int, 2, 3> m;
    Vector<int, 5> v;

    // m.at(0); 
    v.at(1);
}

C++14:

#include <type_traits>

template<typename T, unsigned N, unsigned M=1>
struct Matrix 
{
    T data[N][M];

    template<typename V = T>
    std::enable_if_t<M == 1, V> at(unsigned index)
    {
        return data[index][0];
    }
};

template <typename T, unsigned N>
using Vector = Matrix<T, N, 1>;

int main()
{
    Matrix<int, 2, 3> m;
    Vector<int, 5> v;

    // m.at(0); 
    v.at(1);
}

C++20 (Thanks to Jarod42):

template<typename T, unsigned N, unsigned M = 1>
struct Matrix 
{
    T data[N][M];

    T at(unsigned index) requires (M == 1)
    {
        return data[index][0];
    }
};

template <typename T, unsigned N>
using Vector = Matrix<T, N, 1>;

int main()
{
    Matrix<int, 2, 3> m;
    Vector<int, 5> v;

    // m.at(0); 
    v.at(1);
}

godbolt

  • I never got the SFINAE solution to work myself, so I thought it's impossible. Apparently not. Thanks, I accepted this answer because it's more elegant and `Vector` is literally the same type as `Matrix`, not a derived type. – Jan Schultke May 24 '20 at 21:13