Last Update:
Details of std::mdspan from C++23

Table of Contents
In this article, we’ll see details of std::mdspan,
a new view type tailored to multidimensional data. We’ll go through type declaration, creation techniques, and options to customize the internal functionality.
Type declaration
The type is declared in the following way:
template<
class T,
class Extents,
class LayoutPolicy = std::layout_right,
class AccessorPolicy = std::default_accessor<T>
> class mdspan;
And it has its own header <mdspan>
.
The main proposal for this feature can be found at https://wg21.link/P0009
Following the pattern from std::span
, we have a few options to create mdspan
: with dynamic or static extent.
The key difference is that rather than just one dimension, we can specify multiple. The declaration also gives more options in the form of LayoutPolicy
and AccessorPolicy
. More on that later.
Static Extent
std::vector<int> vec = {1, 2, 3, 2, 4, 5, 3, 5, 6, 7, 8, 9};
std::mdspan<int, std::extents<size_t, 3, 3>> mat3x3 { vec.data() };
std::print("sizeof: {}, rank: {}, ext 0: {}, ext 1: {}\n",
sizeof(mat3x3), mat3x3.rank(), mat3x3.extent(0), mat3x3.extent(1));
The output:
sizeof: 8, rank: 2, ext 0: 3, ext 1: 3
Dynamic Extent
And the dynamic option:
std::mdspan<int, std::dextents<size_t, 2>> mat2x2 { vec.data(), 2, 2 };
std::print("sizeof: {}, rank: {}, ext 0: {}, ext 1: {}\n",
sizeof(mat2x2), mat2x2.rank(), mat2x2.extent(0), mat2x2.extent(1));
The output:
sizeof: 24, rank: 2, ext 0: 2, ext 1: 2
The line:
std::mdspan<int, std::dextents<size_t, 2>>
Is equivalent:
std::mdspan<int, std::extents<size_t, std::dynamic_extents, std::dynamic_extents>>
In other words, dextents
stands for dynamic extents
.
And as of C++26, we can also simplify it with std::dims
:
std::mdspan<int, std::dims<2>> mat2x2 { vec.data(), 2, 2 };
Sizeof mdspan
As you can see, similarly to std::span
, when the size is known at compile time, the object can be smaller (it doesn’t have to store the dynamic size).
Run here: @Compiler Explorer
Construction options
Let’s review some more options to create mdspan
:
1. Default Constructor
constexpr mdspan();
Example:
std::mdspan<int, std::dextents<size_t, 1>> empty_span;
std::print("is empty {}\n", empty_span.empty());
2. Extents From Variadic Arguments
template< class... OtherIndexTypes >
constexpr explicit mdspan( data_handle_type p, OtherIndexTypes... exts );
Example:
int arr[12] { 0 };
std::mdspan<int, std::extents<size_t, 3, 4>> span(arr);
std::print("w: {}, h: {}\n", span.extent(0), span.extent(1));
auto ex = span.extents();
std::println("{}, {}", ex.rank(), ex.static_extent(1));
std::mdspan<int, std::dextents<size_t, 2>> dspan(arr, 3, 4);
std::print("w: {}, h: {}\n", dspan.extent(0), dspan.extent(1));
auto ex2 = dspan.extents();
std::println("{}, {}", ex2.rank(), ex2.static_extent(1));
3. Extents From std::array
template< class OtherIndexType, std::size_t N >
constexpr explicit(N != rank_dynamic()) mdspan( data_handle_type p, const std::array<OtherIndexType, N>& exts );
Example:
int arr[12] { 0 };
std::array<size_t, 2> my_extents = {3, 4};
std::mdspan<int, std::extents<size_t, 3, 4>> span(arr, my_extents);
or simpler with CTAD:
std::mdspan span(arr, my_extents); // but this time extends are dynamic
Layout
Since we’re dealing with multidimensional data, there are a couple of ways we can access elements. That’s why we have the “layout” policy class to represent various options.
Here are the basic layouts available as of C++23:
layout_right
: C or C++ style, row major, where the rightmost index gives stride-1 access to the underlying memory;layout_left
: Fortran or Matlab style, column major, where the leftmost index gives stride-1 access to the underlying memory;layout_stride
: a generalization of the two layouts above, which stores a separate stride (possibly not one) for each extent.
Plus, as of C++26, we’ll get two more options:
layout_left_padded
- column-major layout mapping policy with padding stride that can be greater than or equal to the leftmost extentlayout_right_padded
- row-major layout mapping policy with padding stride that can be greater than or equal to the rightmost extent
Here’s an example:
template <typename layout>
void printMat(std::mdspan<int, std::dextents<size_t, 2>, layout> mat) {
for (size_t i = 0; i < mat.extent(0); ++i) {
for (size_t j = 0; j < mat.extent(1); ++j)
std::print("{} ", mat[i, j]);
std::println();
}
}
int main() {
std::vector<int> vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
std::mdspan<int, std::dextents<size_t, 2>, std::layout_right> mat { vec.data(), 2, 3 };
std::println("right:");
printMat(mat);
std::mdspan<int, std::dextents<size_t, 2>, std::layout_left> mat_left { vec.data(), 2, 3 };
std::println("left:");
printMat(mat_left);
}
The output:
right:
0 1 2
3 4 5
left:
0 2 4
1 3 5
Summary
std::mdspan
provides a powerful and efficient way to interact with multidimensional data, which was a huge pain point for C++. While sometimes it is hard to spell out the full type, mdspan
is very flexible and offers many handy ways to work with data.
In the text, I also showed how to use layout policy for a different way to order elements in the view. The missing part is “AccessorPolicy” which is a policy class that specifies how each element is accessed. For example we can implement a “checked” policy that would ensure the index is not out of range. But I’ll leave that for later considerations.
Back to you
- Have you played with
mdspan
? - What do you use to work with multidimensional data?
Share your comments below
I've prepared a valuable bonus for you!
Learn all major features of recent C++ Standards on my Reference Cards!
Check it out here: