0

I am trying to implement a custom class which handles n-dimensional matrices using vectors of vectors, generalizing the 1d, 2d and 3d definitions:

using mat1dd = std::vector<double>;
using mat2dd = std::vector<mat1dd>;
using mat3dd = std::vector<mat2dd>;

Here is my attempt to implement this class.

template <std::size_t N_DIM, typename T>
class matnd {
    std::vector<matnd<N_DIM - 1, T>> mat;
    ...
}

My intention is that, for example,

matnd<3, double> mat;

creates a 3-dimensional matrix of doubles.

The recursive definition above clearly fails because it has no base case, so N_DIM just decreases indefinitely (until the compiler halts). How could I implement this class in such a way that I do not encounter this problem?

  • 2
    I'd recommend using a 1D vector under the hood. – HolyBlackCat Feb 07 '21 at 10:39
  • @HolyBlackCat could you please give more details about what you mean? I don't understand your suggestion. – Just_a_newbie Feb 07 '21 at 10:42
  • 1
    Just `std::vector mat;`. E.g. if you have a 2D matrix of size 3x4, the vector would have size 3*4=12. – HolyBlackCat Feb 07 '21 at 10:43
  • @HolyBlackCat oh, I understand you now. Wouldn't this potentially make some operations a bit complicated? – Just_a_newbie Feb 07 '21 at 10:52
  • 1
    Nope. You just need to write a single to function to convert N-dimensional coodinates to a position in such vector, everything else is the same. – HolyBlackCat Feb 07 '21 at 10:55
  • `T& operator()(size_t y, size_t x) { return data[y * dim_x_size + x]; }` for a 2d vector for example. – Ted Lyngmo Feb 07 '21 at 10:57
  • see [ND vector and square matrix math template](https://stackoverflow.com/a/55439139/2521214) it does not answer your question (vector and matrices are just 1D and 2D the ND stands for ND geometry) even if it uses the same approach as the Answer here however you can find some inspiration there especially check the recursive determinant template `matrix::det()`... – Spektre Feb 08 '21 at 09:23

1 Answers1

2

Use a template struct holding the type definition specialize it for n = 1:

template<size_t n, typename T>
struct matnd_helper
{
    using type = std::vector<typename matnd_helper<n - 1, T>::type>;
};

template<typename T>
struct matnd_helper<1, T>
{
    using type = std::vector<T>;
};

template<size_t n, typename T>
using matnd = typename matnd_helper<n, T>::type;

using mat1dd = matnd<1, double>;
using mat2dd = matnd<2, double>;
using mat3dd = matnd<3, double>;


fabian
  • 80,457
  • 12
  • 86
  • 114
  • Very interesting approach. Does this mean that I would have to implement n = 1 and n > 1 separately, however? – Just_a_newbie Feb 07 '21 at 11:18
  • Basically yes, but there may already exist functionality that provides the base case, e.g. `template std::vector operator+(const std::vector& v1, const std::vector& v2) { std::vector res; for (size_t i = 0; i < v1.size(); ++i) { res.push_back(v1[i] + v2[i]); } return res; }` would already be sufficient for adding any matrix with a base type providing a binary + operator, since v1 + v2 uses this operator once you reach the base case. – fabian Feb 07 '21 at 11:36