117 std::span
作者Lou Xiao, deepseek创建时间2025-04-02 17:44:58更新时间2025-04-02 17:44:58
1. 概述
std::span
是 C++20 引入的一个轻量级非拥有式容器视图,用于表示连续内存区域的引用。
1.1 基本特性
- 非拥有式:不管理所引用内存的生命周期
- 连续内存:只能表示连续内存序列(如数组、vector等)
- 运行时或编译时大小:可以是固定大小或动态大小
- 轻量级:通常实现为指针+大小的组合
1.2 主要用途
- 函数参数传递(替代指针+大小或首尾迭代器对)
- 统一处理不同类型的连续容器(数组、vector、array等)
- 避免不必要的容器拷贝
2. 基本语法
2.1 头文件
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <span>
2.2 构造方式
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 从数组
2
int arr[] = {1, 2, 3};
3
std::span<int> s1(arr);
4
5
// 从vector
6
std::vector<int> vec = {1, 2, 3};
7
std::span<int> s2(vec);
8
9
// 从指针和大小
10
int* ptr = arr;
11
std::span<int> s3(ptr, 3);
12
13
// 从其他span的子范围
14
std::span<int> s4 = s1.subspan(1, 2);
3. 模板参数
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
template<
2
class T,
3
std::size_t Extent = std::dynamic_extent
4
> class span;
3.1 模板参数说明
T
:元素类型Extent
:范围大小(默认为dynamic_extent
表示动态大小)
3.2 固定大小span
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
std::span<int, 3> fixed_span(arr); // 必须恰好3个元素
4. 成员函数和操作
4.1 元素访问
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
s.front(); // 首元素
2
s.back(); // 末元素
3
s[0]; // 下标访问
4
s.data(); // 底层指针
4.2 容量查询
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
s.size(); // 元素数量
2
s.size_bytes();// 字节大小
3
s.empty(); // 是否为空
4.3 迭代器支持
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
s.begin();
2
s.end();
3
s.rbegin();
4
s.rend();
4.4 子视图操作
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
s.first<3>(); // 前N个元素(编译时大小)
2
s.first(3); // 前N个元素(运行时大小)
3
s.last<3>(); // 后N个元素
4
s.subspan(1, 2); // 从偏移1开始的2个元素
5. 使用场景
5.1 函数参数传递
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
void process(std::span<const int> data) {
2
for (int val : data) {
3
// 处理元素
4
}
5
}
6
7
// 可以接受各种连续容器
8
process(std::vector{1, 2, 3});
9
process(std::array{4, 5, 6});
10
process({7, 8, 9}); // 初始化列表
5.2 与C风格API互操作
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
void legacy_api(int* data, size_t size);
2
3
void wrapper(std::span<int> data) {
4
legacy_api(data.data(), data.size());
5
}
5.3 多维度视图
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 2D视图示例
2
void process_image(std::span<std::span<const Pixel>> image) {
3
for (auto row : image) {
4
for (auto pixel : row) {
5
// 处理像素
6
}
7
}
8
}
6. 注意事项
6.1 生命周期管理
span
不拥有数据,必须确保底层数据在span
使用期间有效- 避免返回局部变量的
span
6.2 类型转换
- 可以隐式转换为
span<const T>
- 不能从
const
转换为非const
6.3 性能
- 通常编译为与原始指针相同的代码
- 无运行时开销
7. 与类似工具比较
特性 | std::span | std::string_view | std::vector |
---|---|---|---|
拥有数据 | 否 | 否 | 是 |
可修改元素 | 是 | 否 | 是 |
连续内存 | 是 | 是 | 是 |
支持非字符类型 | 是 | 否 | 是 |
8. 示例代码
8.1 基本使用
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <span>
2
#include <vector>
3
#include <iostream>
4
5
void print(std::span<const int> values) {
6
for (auto v : values) {
7
std::cout << v << ' ';
8
}
9
std::cout << '\n';
10
}
11
12
int main() {
13
int arr[] = {1, 2, 3};
14
std::vector<int> vec = {4, 5, 6, 7};
15
16
print(arr); // 1 2 3
17
print(vec); // 4 5 6 7
18
print({8, 9, 10});// 8 9 10
19
20
auto sub = std::span(vec).subspan(1, 2);
21
print(sub); // 5 6
22
}
8.2 固定大小span
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
void process3(std::span<int, 3> data) {
2
// 只接受恰好3个元素的span
3
}
4
5
int main() {
6
int arr[3] = {1, 2, 3};
7
process3(arr); // OK
8
9
std::vector<int> vec = {1, 2, 3};
10
// process3(vec); // 错误:不能从动态span转换为固定span
11
12
std::array<int, 3> arr2 = {1, 2, 3};
13
process3(arr2); // OK
14
}
9. 最佳实践
- 优先使用
span<const T>
作为输入参数,除非需要修改数据 - 避免长期存储
span
,因为它不管理生命周期 - 考虑固定大小span在编译时已知大小的情况下
- 替代指针+大小参数,提高代码安全性和可读性
- 注意const正确性:不能从
span<const T>
转换为span<T>
std::span
常用 API 的整理表格
名称 | 用途 | 原型 | 代码示例 | 注释 |
---|---|---|---|---|
构造函数 | 创建 std::span 对象 | span() noexcept = default; span(pointer ptr, size_type count); | std::span<int> s1; std::span<int> s2(arr, 3); | 默认构造空 span;或从指针和元素数量构造。 |
begin | 返回指向首元素的迭代器 | constexpr iterator begin() const noexcept; | auto it = s.begin(); | 类似容器的 begin() 。 |
end | 返回指向末尾的迭代器 | constexpr iterator end() const noexcept; | for (auto& x : s) { ... } | 配合 begin() 实现范围遍历。 |
size | 返回元素数量 | constexpr size_type size() const noexcept; | size_t n = s.size(); | 返回当前 span 中的元素个数。 |
empty | 检查是否为空 | constexpr bool empty() const noexcept; | if (s.empty()) { ... } | 等价于 size() == 0 。 |
operator[] | 访问指定位置的元素 | constexpr reference operator[](size_type idx) const; | int x = s[0]; | 不进行边界检查(类似数组)。 |
data | 返回指向底层数据的指针 | constexpr pointer data() const noexcept; | int* p = s.data(); | 直接访问原始数组指针。 |
front | 访问首元素 | constexpr reference front() const; | int x = s.front(); | 若为空则未定义行为。 |
back | 访问末尾元素 | constexpr reference back() const; | int x = s.back(); | 若为空则未定义行为。 |
subspan | 创建子视图 | constexpr span subspan(size_type offset, size_type count = dynamic_extent) const; | auto sub = s.subspan(1, 2); | 返回从 offset 开始的 count 个元素的视图。 |
size_bytes | 返回存储的字节数 | constexpr size_type size_bytes() const noexcept; | size_t bytes = s.size_bytes(); | 等价于 size() * sizeof(element_type) 。 |
first | 获取前 N 个元素的视图 | constexpr span first(size_type count) const; | auto first3 = s.first(3); | 若 count > size() 则未定义行为。 |
last | 获取后 N 个元素的视图 | constexpr span last(size_type count) const; | auto last2 = s.last(2); | 若 count > size() 则未定义行为。 |
完整示例代码
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <span>
2
#include <iostream>
3
4
int main() {
5
int arr[] = {1, 2, 3, 4, 5};
6
std::span<int> s(arr); // 自动推导长度
7
8
// 遍历
9
for (auto x : s) { std::cout << x << " "; }
10
11
// 子视图
12
auto sub = s.subspan(1, 3); // {2, 3, 4}
13
14
// 访问元素
15
std::cout << "\nFirst: " << s.front() << ", Last: " << s.back();
16
17
// 原始指针
18
int* p = s.data();
19
}
关键注释
- 轻量级:
std::span
不拥有数据,仅作为视图。 - 边界安全:
operator[]
无边界检查,需用户保证安全。 - 动态/静态长度:支持编译时固定长度(
span<int, 5>
)或运行时动态长度(span<int>
)。
文章目录