I'm trying to initialize my Matrix
class with std::initializer_list
s. I know I can do it with std::index_sequence
, but I don't know how to expand them in one statement.
This is how I do it:
template<size_t rows, size_t cols>
class Matrix {
public:
Matrix(std::initializer_list<std::initializer_list<float>> il)
: Matrix(il,
std::make_index_sequence<rows>(),
std::make_index_sequence<cols>()) {}
private:
template<size_t... RowIs, size_t... ColIs>
Matrix(std::initializer_list<std::initializer_list<float>> il,
std::index_sequence<RowIs...>,
std::index_sequence<ColIs...>)
: values_{
{
il.begin()[RowIs].begin()[ColIs]...
}...
} {}
public:
float values_[rows][cols] = {};
};
It fails on the second expansion with error Pack expansion does not contain any unexpanded parameter packs
. Maybe I can somehow specify which parameter pack I want to expand?
Hope for your help!
CodePudding user response:
I think the problem comes from the fact that RowIs
and ColIs
are expanded at the same time, i.e. both always having the same values during the initialization: 0, 1, 2...
You can check here that your current output (after fixing the compiler error) would be something like
[[1.1, 5.5, 9.9], [0, 0, 0], [0, 0, 0]]
for the matrix below:
Matrix<3, 3> m{
{ 1.1, 2.2, 3.3 },
{ 4.4, 5.5, 6.6 },
{ 7.7, 8.8, 9.9 }
};
Because you only read fromil[0,0]
, il[1,1]
, and il[2,2]
in order to set values_
.
What you could do is to create a sequence with a flat index, from 0
to Rows*Cols - 1
, and read every value from il
with FlatIs/Rows
and FlatIs%Cols
:
#include <array>
#include <cstdint>
#include <fmt/ranges.h>
#include <initializer_list>
#include <utility>
template<std::size_t Rows, std::size_t Cols>
class Matrix {
public:
Matrix(std::initializer_list<std::initializer_list<float>> il)
: Matrix(il, std::make_index_sequence<Rows*Cols>())
{}
private:
template<std::size_t... FlatIs>
Matrix(std::initializer_list<std::initializer_list<float>> il,
std::index_sequence<FlatIs...>)
: values_{il.begin()[FlatIs/Rows].begin()[FlatIs%Cols]...}
{}
public:
std::array<std::array<float, Cols>, Rows> values_;
};
int main() {
Matrix<3, 3> m{
{ 1.1, 2.2, 3.3 },
{ 4.4, 5.5, 6.6 },
{ 7.7, 8.8, 9.9 }
};
fmt::print("{}", m.values_);
}
// Outputs:
//
// [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [7.7, 8.8, 9.9]]
CodePudding user response:
If you have access to C 20, you can have the constructor take a (const
) reference to a 2-dimensional C-style array of rows
x cols
elements. Then you can expand on the rows only and let std::to_array
do the rest for you:
#include <array>
#include <cstdint>
template <std::size_t rows, std::size_t cols>
class Matrix {
public:
Matrix(float const (&il)[rows][cols])
: Matrix(il, std::make_index_sequence<rows>()) {}
private:
template <std::size_t... RowIs>
Matrix(float const (&il)[rows][cols], std::index_sequence<RowIs...>)
: values_{std::to_array(il[RowIs])...} {}
public:
std::array<std::array<float, cols>, rows> values_;
};
int main() {
Matrix<2, 3> m({{1, 2, 3}, {4, 5, 6}});
}
If you don't have access to std::to_array
, it's pretty easy to implement it yourself (outside of namespace std
).