Skip to main content
Updated for C++23
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86

There are twothree general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both:

  • One for dimensions known at compile-time.
  • One for dimensions known at run-time and using < C++23
  • One for dimensions known at run-time and using >= C++23

All answers assume you want uniform, two-dimensional arrays (not jagged ones).

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;
// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

Run time-time dimensions (< C++23)

The best way to accomplish a 2 dimensional array with sizes only known at runtimerun-time is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

Run-time dimensions (C++23)

For C++23, there is std::mdspan. This view generalizes a contiguous region of memory into a multi-dimension view. For instance, a 1x12 region of memory could be a 4x3 or 2x3x2, and so on, as long as the view actually makes sense on top of the underlying memory region. Here's an example:

#include <mdspan>
#include <print>
#include <vector>

int main() {
  std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

  // View data as contiguous memory representing 2 rows of 6
  // ints each
  auto ms2 = std::mdspan(v.data(), 2, 6);
  // View the same data as a 3D array 2 x 3 x 2
  auto ms3 = std::mdspan(v.data(), 2, 3, 2);

  // write data using 2D view
  for (size_t i = 0; i != ms2.extent(0); i++)
    for (size_t j = 0; j != ms2.extent(1); j++)
      ms2[i, j] = i * 1000 + j;

  // read back using 3D view
  for (size_t i = 0; i != ms3.extent(0); i++) {
    std::println("slice @ i = {}", i);
    for (size_t j = 0; j != ms3.extent(1); j++) {
      for (size_t k = 0; k != ms3.extent(2); k++)
        std::print("{} ", ms3[i, j, k]);
      std::println("");
    }
  }
}

There are two general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both answers assume you want uniform, two-dimensional arrays (not jagged ones).

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

Run time dimensions

The best way to accomplish a 2 dimensional array with sizes only known at runtime is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

There are three general techniques that I would recommend for this in C++11 and above:

  • One for dimensions known at compile-time.
  • One for dimensions known at run-time and using < C++23
  • One for dimensions known at run-time and using >= C++23

All answers assume you want uniform, two-dimensional arrays (not jagged ones).

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

Run-time dimensions (< C++23)

The best way to accomplish a 2 dimensional array with sizes only known at run-time is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

Run-time dimensions (C++23)

For C++23, there is std::mdspan. This view generalizes a contiguous region of memory into a multi-dimension view. For instance, a 1x12 region of memory could be a 4x3 or 2x3x2, and so on, as long as the view actually makes sense on top of the underlying memory region. Here's an example:

#include <mdspan>
#include <print>
#include <vector>

int main() {
  std::vector v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

  // View data as contiguous memory representing 2 rows of 6
  // ints each
  auto ms2 = std::mdspan(v.data(), 2, 6);
  // View the same data as a 3D array 2 x 3 x 2
  auto ms3 = std::mdspan(v.data(), 2, 3, 2);

  // write data using 2D view
  for (size_t i = 0; i != ms2.extent(0); i++)
    for (size_t j = 0; j != ms2.extent(1); j++)
      ms2[i, j] = i * 1000 + j;

  // read back using 3D view
  for (size_t i = 0; i != ms3.extent(0); i++) {
    std::println("slice @ i = {}", i);
    for (size_t j = 0; j != ms3.extent(1); j++) {
      for (size_t k = 0; k != ms3.extent(2); k++)
        std::print("{} ", ms3[i, j, k]);
      std::println("");
    }
  }
}
Add missing comma.
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86

There are two general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both answers assume you want uniform, two-dimensional arrays (not jagged ones).

Compile time dimensions

Use a std::array of std::array and then use new to put it on the heap:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

Again, this only works if the sizes of the dimensions are known at compile time.

Run time dimensions

The best way to accomplish a 2 dimensional array with sizes only known at runtime is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

 matrix shown in logical form and one-dimensional form

(Taken from http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/)

A contiguous sequence of memory is good for performance reasons and is also easy to clean up. Here's an example class that omits a lot of useful methods but shows the basic idea:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows},
        _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

So we create an array with std::make_unique<int[]>(rows * columns) entries. We overload operator [] which will index the row for us. It returns an int * which points to the beginning of the row, which can then be dereferenced as normal for the column. Note that make_unique first ships in C++14 but you can polyfill it in C++11 if necessary.

It's also common for these types of structures to overload operator() as well:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Technically I haven't used new here, but it's trivial to move from std::unique_ptr<int[]> to int * and use new/delete.

There are two general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both answers assume you want uniform, two-dimensional arrays (not jagged ones).

Compile time dimensions

Use a std::array of std::array and then use new to put it on the heap:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX> sizeY>;
grid * ary = new grid;

Again, this only works if the sizes of the dimensions are known at compile time.

Run time dimensions

The best way to accomplish a 2 dimensional array with sizes only known at runtime is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

 matrix shown in logical form and one-dimensional form

(Taken from http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/)

A contiguous sequence of memory is good for performance reasons and is also easy to clean up. Here's an example class that omits a lot of useful methods but shows the basic idea:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows},
        _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

So we create an array with std::make_unique<int[]>(rows * columns) entries. We overload operator [] which will index the row for us. It returns an int * which points to the beginning of the row, which can then be dereferenced as normal for the column. Note that make_unique first ships in C++14 but you can polyfill it in C++11 if necessary.

It's also common for these types of structures to overload operator() as well:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Technically I haven't used new here, but it's trivial to move from std::unique_ptr<int[]> to int * and use new/delete.

There are two general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both answers assume you want uniform, two-dimensional arrays (not jagged ones).

Compile time dimensions

Use a std::array of std::array and then use new to put it on the heap:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

Again, this only works if the sizes of the dimensions are known at compile time.

Run time dimensions

The best way to accomplish a 2 dimensional array with sizes only known at runtime is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

 matrix shown in logical form and one-dimensional form

(Taken from http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/)

A contiguous sequence of memory is good for performance reasons and is also easy to clean up. Here's an example class that omits a lot of useful methods but shows the basic idea:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows},
        _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

So we create an array with std::make_unique<int[]>(rows * columns) entries. We overload operator [] which will index the row for us. It returns an int * which points to the beginning of the row, which can then be dereferenced as normal for the column. Note that make_unique first ships in C++14 but you can polyfill it in C++11 if necessary.

It's also common for these types of structures to overload operator() as well:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Technically I haven't used new here, but it's trivial to move from std::unique_ptr<int[]> to int * and use new/delete.

Tweak clang-format
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86

There are two general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both answers assume you want uniform, two-dimensional arrays (not jagged ones).

Compile time dimensions

Use a std::array of std::array and then use new to put it on the heap:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX> sizeY>;
grid * ary = new grid;

Again, this only works if the sizes of the dimensions are known at compile time.

Run time dimensions

The best way to accomplish a 2 dimensional array with sizes only known at runtime is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

 matrix shown in logical form and one-dimensional form

(Taken from http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/)

A contiguous sequence of memory is good for performance reasons and is also easy to clean up. Here's an example class that omits a lot of useful methods but shows the basic idea:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows}, _columns{columns}, data{std::make_unique<int[]>(
                                    _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

So we create an array with std::make_unique<int[]>(rows * columns) entries. We overload operator [] which will index the row for us. It returns an int * which points to the beginning of the row, which can then be dereferenced as normal for the column. Note that make_unique first ships in C++14 but you can polyfill it in C++11 if necessary.

It's also common for these types of structures to overload operator() as well:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Technically I haven't used new here, but it's trivial to move from std::unique_ptr<int[]> to int * and use new/delete.

There are two general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both answers assume you want uniform, two-dimensional arrays (not jagged ones).

Compile time dimensions

Use a std::array of std::array and then use new to put it on the heap:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX> sizeY>;
grid * ary = new grid;

Again, this only works if the sizes of the dimensions are known at compile time.

Run time dimensions

The best way to accomplish a 2 dimensional array with sizes only known at runtime is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

 matrix shown in logical form and one-dimensional form

(Taken from http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/)

A contiguous sequence of memory is good for performance reasons and is also easy to clean up. Here's an example class that omits a lot of useful methods but shows the basic idea:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows}, _columns{columns}, data{std::make_unique<int[]>(
                                            rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }
}

So we create an array with std::make_unique<int[]>(rows * columns) entries. We overload operator [] which will index the row for us. It returns an int * which points to the beginning of the row, which can then be dereferenced as normal for the column. Note that make_unique first ships in C++14 but you can polyfill it in C++11 if necessary.

It's also common for these types of structures to overload operator() as well:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Technically I haven't used new here, but it's trivial to move from std::unique_ptr<int[]> to int * and use new/delete.

There are two general techniques that I would recommend for this in C++11 and above, one for compile time dimensions and one for run time. Both answers assume you want uniform, two-dimensional arrays (not jagged ones).

Compile time dimensions

Use a std::array of std::array and then use new to put it on the heap:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX> sizeY>;
grid * ary = new grid;

Again, this only works if the sizes of the dimensions are known at compile time.

Run time dimensions

The best way to accomplish a 2 dimensional array with sizes only known at runtime is to wrap it into a class. The class will allocate a 1d array and then overload operator [] to provide indexing for the first dimension. This works because in C++ a 2D array is row-major:

 matrix shown in logical form and one-dimensional form

(Taken from http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/)

A contiguous sequence of memory is good for performance reasons and is also easy to clean up. Here's an example class that omits a lot of useful methods but shows the basic idea:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows},
        _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

So we create an array with std::make_unique<int[]>(rows * columns) entries. We overload operator [] which will index the row for us. It returns an int * which points to the beginning of the row, which can then be dereferenced as normal for the column. Note that make_unique first ships in C++14 but you can polyfill it in C++11 if necessary.

It's also common for these types of structures to overload operator() as well:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

Technically I haven't used new here, but it's trivial to move from std::unique_ptr<int[]> to int * and use new/delete.

Format using clang-format
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Add `operator()`
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Fix rows and columns.
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
note that `make_unique` is part of C++14
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Describe image.
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Change formatting so it fits without scrolling on smaller screens or larger fonts.
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Inline constructor as VS has issues with it.
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Use C++11 brace initialization
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Remove const
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Explain simplified class a bit more (was sort of removed by previous edit)
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Return by const reference (helpful in some situations)
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Clarify some assumptions.; added 3 characters in body
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Expanded explanations.
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading
Source Link
Levi Morrison
  • 19.4k
  • 8
  • 66
  • 86
Loading