kubci98
kubci98

Reputation: 398

How to initialize a 2D C-style array from a nested std::initializer_list?

I am creating a Matrix<type, width, height> class which I want to be able to initialize using initializer_list, for instance:

Matrix<int, 2, 2> mat = Matrix<int, 2, 2>{ {1,2},{3,4} };

The matrix is implemented using a T[height][width] 2D array.

To do this, I have tried making a constructor such as:

Matrix(std::initializer_list< std::initializer_list<T>> input);

However, I do not know how to fill the array with from the list. I have tried

Is there any other way, preferably as safe as possible, to do this?

Upvotes: 2

Views: 266

Answers (2)

Jon Daniel
Jon Daniel

Reputation: 1

another n-dimensional example without using std::array in the initializer list

#include <iostream>
#include <array>            // std::array
#include <initializer_list> // std::initializer_list
#include <algorithm>        // std::copy

template<typename T>
concept scalar = std::arithmetic<T> || std::floating_point<T>;

namespace mat
{
template<scalar T, size_t COLS, size_t ROWS>
struct type : std::array<vec::type<T, ROWS>, COLS>
{
   type(std::initializer_list<T[ROWS]> src)
   {
       auto iter = src.begin();
       for(size_t cols = 0; (cols < COLS) && (iter != src.end()); ++cols)
          (*this)[cols] = *reinterpret_cast<const vec::type<T, ROWS>*>(iter++);
   }
   vec::type<T, ROWS>& column(size_t i) { return (*this)[i % COLS]; }
   vec::type<T, COLS>  row(size_t i)
   {
       vec::type<T, COLS> dst;
       for(size_t j = 0; j < COLS; j++)
          dst[j] = (*this)[j][i % ROWS];
       return dst;
   }
};
};

Upvotes: 0

JeJo
JeJo

Reputation: 32952

You should provide exact type to the Matrix(std::initializer_list</* here */> list);. Then to fill the array, you need to iterate through the list passed and fill the array. (See live online)

#include <initializer_list> // std::initializer_list

template<typename T, std::size_t R, std::size_t C>
class Matrix
{
    T mArray2D[R][C]{};

public:
    Matrix(const std::initializer_list<T[C]> list)
    //                                 ^^^^^ --> type of the row
    {
        auto iter{ list.begin() }; // returns pointer to T[C]
        for (std::size_t row{}; row < R; ++row)
        {
            const auto& rowElements{ *iter };
            for (std::size_t col{}; col < C; ++col)
            {
                mArray2D[row][col] = rowElements[col];
            }
            ++iter;
        }
    }
};

Now in the main() you can initialize Matrix using an initializer list as follows:

Matrix<int, 2, 2> mat{ {1,2}, {3,4} };
// or
// Matrix<int, 2, 2> mat = { {1,2}, {3,4} };

If you could use std::array, you could do the following. (See live online)

#include <iostream>
#include <array>            // std::array
#include <initializer_list> // std::initializer_list

template<typename T, std::size_t R, std::size_t C>
class Matrix
{
    using RowType = std::array<T, C>;
    T mArray2D[R][C]{};

public:
    Matrix(const std::initializer_list<RowType> list)
    {
        auto iter{ list.begin() };
        for (std::size_t row{}; row < R; ++row)
        {
            const RowType& rowElements{ *iter };
            for (std::size_t col{}; col < C; ++col)
            {
                mArray2D[row][col] = rowElements[col];
            }
            ++iter;
        }
    }
};

If the multidimensional array in the class could be an std::array of std::array of type T, you could use std::copy as well. (See live online)

#include <iostream>
#include <array>            // std::array
#include <initializer_list> // std::initializer_list
#include <algorithm>        // std::copy

template<typename T, std::size_t R, std::size_t C>
class Matrix
{
    using RowType = std::array<T, C>;
    using Array2D = std::array<RowType, R>;
    Array2D mArray{ RowType{} }; // initialize the matrix

public:
    Matrix(const std::initializer_list<RowType> list)
    {
       std::copy(list.begin(), list.end(), mArray.begin());
    }
};

Upvotes: 5

Related Questions