std::mdspan是C++23引入的轻量级多维数组视图,不拥有数据,仅持指针、布局和尺寸;构造需三要素(指针、extents、layout)且指针生命周期须覆盖mdspan;下标访问用operator()而非嵌套operator[];函数传参宜用模板推导;与拥有数据的std::mdarray定位不同;需编译器和标准库支持。

std::mdspan 是 C++23 引入的轻量级多维数组视图,它不拥有数据,只持有指向内存的指针 + 布局信息(layout)+ 尺寸(extents),类似 std::span 的多维扩展。它本身不分配内存,也不管理生命周期,用错最常见后果是悬空访问或越界读写——不是编译报错,而是运行时未定义行为。
怎么构造一个合法的 std::mdspan
构造核心是三要素:数据指针、尺寸描述(std::extents)、布局策略(std::layout_left/std::layout_right/std::layout_stride)。缺一不可,且指针必须保证在 mdspan 生命周期内有效。
常见错误:
- 传入栈上局部数组地址,但
mdspan逃逸出作用域 → 悬空 - 用
std::vector::data()构造后,又对 vector 调用push_back或resize→ 迭代器失效,指针失效 - 尺寸参数顺序与实际内存布局不匹配(比如按行主序传了列数在前)→ 索引错位
正确示例(二维行主序):
立即学习“C++免费学习笔记(深入)”;
int data[12] = {0};
std::extents<size_t, 3, 4> ext; // 3 行 × 4 列
std::mdspan<int, std::extents<size_t, 3, 4>, std::layout_right> view{data, ext};
view(1, 2) = 42; // 等价于 data[1 * 4 + 2]
std::mdspan 的下标访问和 operator[] 差异
mdspan 支持多参数 operator()(如 view(i, j, k)),也支持单参数 operator[],但后者只对一维视图或降维后视图有意义。误用会导致编译失败或意外降维。
关键点:
-
view[i]对二维mdspan返回的是mdspan<T, std::extents<size_t, N>>(即“第 i 行”的切片视图),不是T& - 要取元素必须用
view(i, j),不能写view[i][j](除非你显式重载了嵌套[],标准库没提供) -
view(i...)参数个数必须严格等于秩(rank),少一个或多一个都编译不过
反例(编译错误):
std::mdspan<double, std::extents<size_t, 2, 3>> v{buf, std::extents<size_t, 2, 3>{}};
auto row = v[0]; // OK:row 是 mdspan<double, std::extents<size_t, 3>>
double x = v[0][1]; // ❌ error:row.operator[] 接受 size_t,返回 double&?不,它返回 mdspan<...>,而该类型无 operator[]如何安全传递 std::mdspan 给函数
函数参数应优先使用模板形参推导,避免丢失秩和布局信息。直接写死类型(如 std::mdspan<float, std::extents<size_t, 4, 4>>)会失去泛化能力,且无法接受不同尺寸或布局的视图。
推荐方式:
- 用模板参数约束
std::mdspan类型:template<class T, class Extents, class Layout> void process(std::mdspan<T, Extents, Layout> s) - 若只需读取,可加
const:std::mdspan<const float, ...>,防止意外修改底层数据 - 避免值传递大尺寸
mdspan(虽然它本身很小,但语义上易误导;引用更清晰)
注意:不能用 auto 参数(C++23 不支持),也不能用 std::mdspan<auto> —— 类型参数必须显式指定或由模板推导。
std::mdspan 和 std::mdarray 的关系别搞混
std::mdarray 是 C++23 同批引入的「拥有数据」的多维容器,内部用 std::mdspan 提供视图接口。二者定位完全不同:
-
mdspan是视图(view)→ 零开销抽象,适合函数参数、算法接口、interop 场景(如对接 Fortran 数组或 GPU 内存) -
mdarray是容器(container)→ 自动管理堆内存,支持resize,但目前不支持自定义分配器,也不支持非默认构造元素 - 不要试图用
mdspan替代std::vector<std::vector<T>>来存不规则矩阵——它只支持规则(矩形)布局
真正容易被忽略的一点:当前主流标准库实现(libstdc++ 13+, libc++ 17+)才开始支持 mdspan,MSVC 2022 17.8+ 仅部分支持,且需开启 /std:c++23 和 /experimental:module(某些版本)。头文件是 <mdspan>,不是 <span> 或 <array>。


















