I am trying to make a fixed-size matrix class. The intent is to make it inherit or utilize a std::array
of std::array
:
template <size_t Rows, size_t Columns, class T=double>
struct Matrix : public std::array<std::array<T, Columns>, Rows> {
};
I would like to provide an initializer that can automatically deduce the size, like std::array
can in C++17 (which I am using). I'm also fine with using a function to make the Matrix
instead of using class template argument deduction.
// What I want, but does not work:
Matrix matrix {{1., 2.},
{3., 4.}};
// Or:
auto matrix = MakeMatrix ({1., 2.},
{3., 4.});
I have failed to get either of these to be possible. Instead, only the following works:
// Requires specifying the size and repeating `std::array`, both undesired
Matrix<2,2> mat {
std::array{1., 2.},
std::array{3., 4.}
};
// OR this, which requires specifying the size and is generally confusing
Matrix<2,2> mat2 {1., 2.,
3., 4.};
I tried using variadic templates, but this too did not appeal the compiler:
template<class... Args>
auto MakeMatrix (Args... args) {
return Matrix{ std::array {args} ... };
}
// This causes compiler error:
// error: no matching function for call to 'MakeMatrix'
// candidate function [with Args = <>] not viable: requires 0 arguments, but 2 were provided
auto matrix = MakeMatrix ({1., 2.},
{3., 4.});
// This causes compiler error
// error: no viable constructor or deduction guide for deduction of template arguments of 'Matrix'
// note: in instantiation of function template specialization 'MakeMatrix<std::__1::array<double, 2>, std::__1::array<double, 2> >'
// note: note: candidate function template not viable: requires 0 arguments, but 2 were provided
auto matrix = MakeMatrix (std::array {1., 2.},
std::array {3., 4.});
I've also considered using std::initializer_list<std::initializer_list<T>>
, however these do not support fixed size as far as I can tell, and I want the size determined at compile time.
Any thoughts on how to do this, or is it just impossible with the current C++ mechanics?
C++, Using 2D array (max size 100*26) - LeetCode Discuss.
To declare a 2D array, specify the type of elements that will be stored in the array, then ( [][] ) to show that it is a 2D array of that type, then at least one space, and then a name for the array. Note that the declarations below just name the variable and say what type of array it will reference.
The length of a 2D array is the number of rows it has. The row index runs from 0 to length-1. Each row of a 2D array can have a different number of cells, so each row has its own length : uneven[0] refers to row 0 of the array, uneven[1] refers to row 1, and so on.
For a two-dimensional array, in order to reference every element, we must use two nested loops. This gives us a counter variable for every column and every row in the matrix. For example, we might write a program using a two-dimensional array to draw a grayscale image.
The problem is that the compiler can't deduce {}
when used as an argument. This works with for initializer_list
(for contructors, because of some special rules). But then you are missing the size.
A workaround are built-in arrays:
template <typename T, size_t N>
using Row = const T (&)[N]; // for readability
template <auto Rows, auto Columns, typename T = double>
class Matrix {
public:
template <typename... Ts, auto N>
Matrix(Row<Ts, N>... rows) {}
};
template <typename... RowTypes, auto Columns>
Matrix(Row<RowTypes, Columns>...)
-> Matrix<sizeof...(RowTypes), Columns, std::common_type_t<RowTypes...>>;
You can now construct Matrix
exactly as you like:
const auto m = Matrix{{1, 2}, {1, 2}, {1, 2}};
For the final step, initializing std::array
with build-in arrays can be tricky. C++20 provides a function, check the link for possible implementations. If you copy that implementation, or have one available to you, you can create the constructor easily, as:
template <auto Rows, auto Columns, typename T = double>
class Matrix {
public:
template <typename... Ts, auto N>
Matrix(Row<Ts, N>... rows) {
data_ = {to_array(rows)...};
}
private:
std::array<std::array<T, Columns>, Rows> data_;
};
Live example, with operator[]
to show that the data layout is correct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With