在 C++20 之前,想要在不复制数据的前提下安全地切片数组,常常需要自己手动维护指针和长度,或者使用第三方库。C++20 的 std::span 就是为此而生的一个轻量级视图类型,它把“指针 + 长度”组合成一个对象,提供了更安全、更易用的接口。本文将从 std::span 的基本使用、与 STL 容器的互操作、性能注意点以及常见陷阱等方面进行系统介绍,并给出实用的代码示例。
1. std::span 简介
std::span<T, Extent> 是一个非拥有(non-owning)的视图(view),用来描述一段连续的内存。
T为元素类型。Extent为可选的大小,若为std::dynamic_extent(默认值)则长度是动态的。
核心特点:
- 无复制:只包装了原始指针和长度。
- 范围检查:提供
data()、size()、operator[],并可通过at()进行边界检查。 - 与 STL 兼容:支持
std::begin()/std::end(),可直接用于 range-based for。
2. 基本使用示例
#include <span>
#include <iostream>
#include <vector>
void print(span <int> s) {
for (int v : s) std::cout << v << ' ';
std::cout << '\n';
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::span <int> whole(arr); // 视图整个数组
std::span <int> firstHalf(arr, 5); // 只视图前 5 个元素
print(whole); // 输出: 1 2 3 4 5 6 7 8 9 10
print(firstHalf); // 输出: 1 2 3 4 5
// 可以直接从 std::vector 取 span
std::vector <int> v = {100, 200, 300};
print(v); // std::span is implicitly convertible from std::vector
}
3. 与 STL 容器互操作
3.1 从容器获取 span
std::vector <int> vec{10, 20, 30, 40, 50};
std::span <int> sp(vec); // 自动推断为 vector 的数据和大小
3.2 作为函数参数
void process(span<const int> data) { /* read-only */ }
- 传递
std::span<const int>使得函数只能读操作,避免不必要的修改。
3.3 作为返回值
std::span 本身不拥有数据,返回时必须保证所指向的数据仍然有效。常见做法是返回对内部容器的 span,调用者自行管理生命周期。
std::span<const int> getSubArray(const std::vector<int>& vec, size_t offset, size_t len) {
return std::span<const int>(vec.data() + offset, len);
}
4. 性能与安全注意点
| 位置 | 说明 | 建议 |
|---|---|---|
| 复制 | span 的拷贝是轻量级(仅复制指针+长度) |
频繁传递可通过引用 `span |
&或const span&` |
||
| 生命周期 | 必须确保底层数据在 span 使用期间有效 | 避免返回局部数组的 span;可使用 std::shared_ptr 或 std::vector 管理 |
| 对齐 | span 不进行对齐检查,使用时需保证数据对齐 |
对 SIMD 等要求对齐的场景,可先检查 std::align |
5. 常见陷阱
-
返回临时对象的 span
std::span <int> foo() { int arr[5] = {0}; return std::span <int>(arr); // 错误:arr 在返回时已失效 }解决:返回对持久数据的 span,或返回 `std::vector
`。 -
篡改容器大小后使用旧 span
std::vector <int> v{1,2,3,4}; auto sp = v; // sp 视图整个 vector v.resize(10); // 扩容后,sp 指向旧内存,使用 undefined behavior -
忽略对边界的检查
operator[]不做检查,at()提供检查。若对安全性要求高,建议使用at()或自己加边界校验。
6. 进阶使用:二维 span 与可变 span
6.1 二维 span
C++20 并未直接提供二维 span,但可以用 span<span<T>> 或自定义包装:
using Row = std::span <int>;
std::vector <Row> matrix;