std::span是C++20引入的轻量级非拥有型连续内存视图,用于安全高效地引用数组、vector等连续容器;适用于函数参数避免裸指针+长度、提升类型安全与可读性,但不可管理内存生命周期或绑定临时对象。

std::span 是什么,什么时候该用它
std::span 是 C++20 引入的轻量级非拥有型视图(non-owning view),用来安全、高效地引用一段连续内存(比如数组、std::vector、std::array 或裸指针+长度)。它不管理内存生命周期,只记录起始地址和元素个数,零开销。
适合场景包括:
- 函数参数接收“任意连续容器”,避免模板泛化或重载爆炸
- 替代
const T*+size_t这种容易出错的裸指针组合 - 在接口中明确表达“我只读/只写一段连续数据”,提升可读性和类型安全
别在以下情况硬套:std::span 不能持有动态分配的内存所有权,也不能绑定到临时 std::vector 的 .data()(除非确保生命周期足够长)。
怎么构造 std::span:常见方式与陷阱
构造 std::span 最常用三种方式,但每种都有隐含约束:
立即学习“C++免费学习笔记(深入)”;
从数组推导:
int arr[] = {1, 2, 3};<br>std::span s1{arr}; // OK,自动推导 size = 3
✅ 安全;⚠️ 仅适用于编译期已知大小的数组(std::array同理)从容器的
.data()和.size()构造:std::vector v = {4, 5, 6, 7};<br>std::span s2{v.data(), v.size()}; // OK<br>// 或更简洁:<br>std::span s3{v}; // C++20 要求容器有 .data()/.size() 且是连续的
✅std::vector、std::array、std::string(C++23 起)都支持;⚠️v生命周期必须长于s2,否则悬垂从裸指针 + 长度构造:
int* ptr = new int[5];<br>std::span s4{ptr, 5}; // OK,但你得自己保证 ptr 有效、5 不越界<br>delete[] ptr; // ⚠️ 此后 s4 成为悬垂 span — 编译器不报错,运行时 UB
❌ 不推荐,除非在极底层且生命周期绝对可控;建议优先用容器封装后再转std::span
std::span 的边界检查和迭代操作
std::span 默认不启用运行时边界检查(类似 std::vector::operator[]),下标访问是 O(1) 且无额外开销:
std::span<int> s = {arr};<br>int x = s[0]; // OK,无检查<br>int y = s.at(10); // 抛 std::out_of_range(仅当 _GLIBCXX_DEBUG 或类似调试宏开启时)实际项目中:
- 生产代码依赖静态约束(如模板参数、编译期数组大小)来防越界
- 单元测试可用
assert(s.size() > idx)辅助验证 - 迭代推荐用范围 for:
for (int val : s) { /<em> ... </em>/ }
或手动迭代:for (size_t i = 0; i < s.size(); ++i) { /<em> s[i] </em>/ }
注意:s.begin()/s.end() 返回的是原生指针(T<em></em>),不是迭代器对象,所以不能用 std::sort(s.begin(), s.end()) 以外的算法——等等,这个其实可以,因为 T 就是随机访问迭代器。重点是:它没额外封装,就是指针。
std::span 传参时的 const 和可变性控制
函数签名里用 std::span 可以精确表达读写意图:
void read_only(std::span<const int> s); // 只读,s[i] 返回 const int&<br>void read_write(std::span<int> s); // 可写,s[i] 返回 int&<br>void take_any_span(auto s) requires std::is_same_v<decltype(s), std::span<int>>; // 模板约束更严
关键点:
-
std::span<t></t>和std::span<const t></const>类型不同,不可隐式转换(std::span<int></int>→std::span<const int></const>是允许的,但反向不行) - 若函数内部只读,务必声明
const T版本,否则调用方传std::span<const int></const>会编译失败 - 不要写
std::span<int>&</int>传参——std::span本身小(通常两个指针大小),值传递更自然、更符合其“视图”语义
最常被忽略的一点:跨模块传递 std::span 时,如果对方代码没开 C++20,或者用了旧标准库(如某些嵌入式 STL 实现),std::span 可能根本不存在——得降级用 gsl::span 或自定义简易版,别假设它一定可用。


















