c++c++11templatesmultidimensional-arraystdinitializerlist

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?


Solution

  • 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());
        }
    };