在 C++20 中,std::span 成为标准库中一个极为实用的类型,它为数组、容器以及任意连续内存块提供了一个无侵入的视图。与传统的指针和长度配对不同,std::span 把这两部分封装为一个轻量级对象,既能保持高效,又能提升代码可读性与安全性。本文将从概念、实现细节、典型用法以及常见坑点四个方面展开讨论,帮助你在日常编码中更好地利用 std::span。
1. std::span 的核心概念
- 轻量级视图:std::span 本身只包含两个成员:一个指向数据的指针和一个长度。它不拥有底层内存,也不负责内存管理,因此对象非常小(通常只有 16 字节左右),可以被复制、传递和返回而不会产生额外开销。
- 连续性保证:span 只适用于连续内存区域(如数组、std::vector、std::array 或自定义内存块)。这与 C++ 的标准容器设计哲学保持一致,避免了不安全的非连续访问。
- 类型安全:span 通过模板参数 T 绑定数据类型,编译器会检查传递给 span 的数据类型是否匹配。对于多维数组,std::span 支持多层模板特化,但使用时需手动指定维度。
2. 基本用法示例
#include <span>
#include <vector>
#include <iostream>
void print_span(std::span <int> s) {
for (int v : s) std::cout << v << ' ';
std::cout << '\n';
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
std::span <int> sp1(arr); // 从 C 风格数组构造
std::vector <int> vec{10,20,30,40,50,60};
std::span <int> sp2(vec); // 从 vector 构造(只读)
std::span <int> sp3(vec.data(), 3); // 指定长度
std::span<const int> sp4(vec); // const 视图
print_span(sp1); // 1 2 3 4 5
print_span(sp3); // 10 20 30
// 通过 span 修改底层数据
for (auto& x : sp2) x *= 2;
print_span(sp2); // 20 40 60 80 100 120
return 0;
}
提示:若需要读写访问,使用 `std::span
`;若只需读,使用 `std::span`。这能在编译阶段阻止不恰当的修改。
3. 高级特性
3.1 绑定固定长度
std::span<int, 4> fixed4{arr}; // 必须长度为 4
如果你确定视图的长度在编译时已知,可以使用第二个模板参数限制长度。若不满足,编译错误,提升安全性。
3.2 子 span 与分片
std::span <int> sub = sp2.subspan(2, 3); // 从下标 2 开始,长度 3
subspan 可以用于构造更小的视图,而不复制底层数据。subspan(2) 等价于 subspan(2, s.size() - 2)。
3.3 与字符串视图互操作
C++20 还引入了 std::string_view,其功能与 `std::span