0

I am making a matrix class where I want users to be able to instantiate the matrix like so:

Matrix<float, 2, 2> mat = { { 10, 20 }, { 30, 40 } };

My Matrix class is defined like so:

template<typename T, unsigned int ROWS, unsigned int COLS>
class Matrix
{
public:
    Matrix(std::array<std::array<T, ROWS>, COLS> matrix)
    {
        // ...
    }
    // ...
};

However, when I try instantiating the matrix, as I did above, I get "could not convert" errors from the compiler. I don't want to use initializer lists because I want compile-time errors to be triggered if the user defines the matrix with the wrong order. Does anyone why this isn't working? and if so is there an alternative?

MaximV
  • 155
  • 11
  • 1
    Issue is that `std::array` use aggregate initialization (so might have/require extra brace), but then you have ambiguous call with copy/move constructor :-/ – Jarod42 May 23 '20 at 16:00
  • Thanks @Jarod42, is there any way around it then? – MaximV May 23 '20 at 16:04

3 Answers3

2

std::array only supports aggregate initialization. By using the Matrix<float, 2, 2> mat = { ... }; syntax, you are requesting copy initialization, which std::array just rejects. Note that, by taking a std::array<std::array<...>...> as constructor parameter, you allow the following initialization syntax: Matrix<float, 2, 2> mat{{ 10, 20, 30, 40 }};.

Most likely, what you want is a std::initializer_list parameter.

If you want your class to act like a std::array but with two dimensions, you might as well do what implementations of std::array do, which is having no constructor and making the inner member public:

template<typename T, unsigned int ROWS, unsigned int COLS>
class Matrix
{
public:
    T elements_[ROWS][COLS];
};

Here is the implementation of stdlibc++ (gcc): https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/array#L93. Note the declaration of _M_elems is public (array is a struct here). This means that you could access _M_elems directly if you know you are using this specific implementation of the standard library, but it's also the only way to get aggregate initialization working.

It also means you allow the syntax Matrix<float, 2, 2> mat2 = { 10, 20, 40, 40 };.

Demo

Nelfeal
  • 12,593
  • 1
  • 20
  • 39
  • as said earlier, the problem with std::initializer_list is that I want compile-time errors to be triggered if the matrix is not of the right order. – MaximV May 23 '20 at 16:24
  • @MaximV I added another option you might be interested in. – Nelfeal May 23 '20 at 17:15
1

Issue is that std::array uses aggregate initialization (so might have/require extra brace), but then you might have ambiguous call with copy/move constructor :-/

Following compiles:

template <std::size_t COLS, std::size_t ROWS>
struct Matrix
{
    Matrix(const std::array<std::array<float, COLS>, ROWS>&) {}
    //Matrix(const std::vector<std::vector<float>>&) {}
};

int main() {
    [[maybe_unused]]Matrix<3, 2> m1 = std::array{ std::array{1.f, 2.f, 3.f}, std::array{1.f, 2.f, 3.f}};
    [[maybe_unused]]Matrix<3, 2> m2 ({{ {{1.f, 2.f, 3.f}}, {{1.f, 2.f, 3.f}}}});
    //[[maybe_unused]]Matrix<3, 2> m3 ({ {1.f, 2.f, 3.f}, {1.f, 2.f, 3.f}}); // OK vector
}

Demo

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

This seems to work...

template<typename T, unsigned int ROWS, unsigned int COLS>
class Matrix
{
public:
    Matrix(const std::array<T, ROWS> (&matrix)[COLS]) {
        // ... 
    }
};

int main() {
    Matrix<float, 2, 2> mat = {{ { 10, 20 }, { 40, 40 } }};
}

Though the error message is quite bad when it fails, and it only fails if you provide too many rows or columns!... for the same reason that std::array<int,3> a = {1,2}; is valid...

Edit:

Matrix(const T (&matrix)[COLS][ROWS]) {}

is also valid

Jake Schmidt
  • 1,558
  • 9
  • 16
  • Thanks @JakeSchmidt, this works well, the only thing is I don't understand how the syntax works because I tried using ```Matrix(const std::array[COLS]& matrix)``` instead and it didn't work – MaximV May 23 '20 at 16:33
  • @MaximV https://stackoverflow.com/questions/1328223/when-a-function-has-a-specific-size-array-parameter-why-is-it-replaced-with-a-p – Jake Schmidt May 23 '20 at 16:37
  • Also, might it be possible to pass a c++ array like: ```Matrix(T[COLS][ROWS] matrix)```? – MaximV May 23 '20 at 16:37
  • @MaximV I don't think so – Jake Schmidt May 23 '20 at 16:59