what is the best modern c++ approach to construct and manipulate a 2d array [closed]

Solution 1:

I happen to have this completely untested example lying around. If it works, good, otherwise it is an example of how you might go about creating a 2D view onto a 1D array:

template<typename T>
class two_dee_array
{
public:
    two_dee_array(std::size_t row, std::size_t col)
        : v(row * col), stride(col) {}

    T& operator()(std::size_t row, std::size_t col)
        { return v[(row * stride) + col]; }

    T const& operator()(std::size_t row, std::size_t col) const
        { return v[(row * stride) + col]; }

    std::size_t col_size() const { return stride; }
    std::size_t row_size() const { return v.size() / stride; }

    auto begin() { return std::begin(v); }
    auto end() { return std::end(v); }

    auto begin() const { return std::begin(v); }
    auto end() const { return std::end(v); }

    auto cbegin() const { return std::cbegin(v); }
    auto cend() const { return std::cend(v); }

private:
    std::vector<T> v;
    std::size_t stride;
};

Solution 2:

Some points to get you started.

First, std::array vs std::vector. This is easy. If you know at compile time the size of your 2d array then definitely std::array else std::vector.

std::vector<std::vector<T>> is ok for something that you need to cook up quickly and you use seldom in pieces of code that are not performance critical(1).

The big downside to std::vector<std::vector<T>> is the memory layout. You have double indirection and each line is allocated separately, wreaking havoc on your cache so it's a definitely a no no in performance critical code (1). For performance critical code you need to use a mature math library. Experts can write performance oriented code order of magnitude better than you or me and another important advantage of such library is that it passed the test of time.

If for whatever reason you want to write this yourself then the solution is to flatten the matrix. I.e. use std::vector<T>(numRows * numColumns) and then access the elements with the formula: (i, j) -> v[i * numColumns + j]. And wrap that in a nice interface. How complex the interface is and what kind of operations you allow on it it's totally up to you.


(1) I would like to point out that most often especially novice programmers completely misunderstand performance concerns (it's completely understandable due to lack of experience). First there are some few generally acceptable practices that apply to any situation. Then comes the analysis of the algorithm and the data types used. But other than that you first write code for readability and then, if performance is a concern, you profile your code and start optimizing where the profiler tells you.