Wiki
std::span
1.What problem does this section solve?
You need to write a function that processes a contiguous sequence of data—which can be a vector, array, or C array. Before C++20, you have two options:
- Pass
const std::vector<T>&: Can only receive vector, cannot receive array or C array. - Pass
const T* data, size_t size: Can receive all, but loses size information, prone to errors.
std::span providesa unified read-only (or writable) viewthat does not own data, but is merely a "window" that can uniformly handle all contiguous memory containers.
2.What is this feature?
std::span<T> is introduced by C++20view type; it does not own data, but is a lightweight wrapper consisting of a pointer to a contiguous segment of memory plus a size. You can understand it as a "reference to an array".
3.C++ Standard Edition
C++20
4.Required header files
#include <span>
5.Basic syntax
// span 不拥有数据,只是观察
std::span<T> sp; // 空 span
std::span<T> sp(ptr, size); // 从指针 + 大小构造
std::span<T> sp(vector); // 从 vector 构造
std::span<T> sp(array); // 从 std::array 构造
std::span<T> sp(c_array); // 从 C 数组构造
// 常用操作
sp.size(); // 元素个数
sp.empty(); // 是否为空
sp[i]; // 下标访问
sp.front(); // 第一个元素
sp.back(); // 最后一个元素
sp.data(); // 底层指针
sp.subspan(pos, count); // 子视图
sp.first(n); // 前 n 个元素的子视图
sp.last(n); // 后 n 个元素的子视图
6.Common Usage
| Usage | Description |
|---|---|
void f(std::span<const int> data) |
Accepts any contiguous int container |
std::span<int>(vec) |
Create from vector |
std::span<int>(arr) |
Create from C array |
sp.subspan(offset, count) |
Create a subview (no copy) |
7.span<const T> and span<T> difference
| Usage | Whether underlying data can be modified | Common uses |
|---|---|---|
std::span<const T> |
Cannot be modified | Read-only processing, statistics, printing, validation |
std::span<T> |
Can be modified | Buffer filling, data normalization, in-place filtering |
span You do not own the data, so "whether const" is very important. Function parameters should by default be written as std::span<const T> first, and only written as std::span<T> when the original data is explicitly intended to be modified.
8.Example code
8.1.Example 1: Use span to uniformly accept different container types
#include <iostream>
#include <span>
#include <vector>
#include <array>
// 一个函数处理所有类型的连续 int 数据
void print_data(std::span<const int> data)
{
std::cout << "size = " << data.size() << ": ";
for (int n : data)
{
std::cout << n << " ";
}
std::cout << "\n";
}
int main()
{
int c_arr[] = {1, 2, 3, 4, 5}; // C 数组
std::vector<int> vec = {10, 20, 30}; // vector
std::array<int, 4> arr = {100, 200, 300, 400}; // std::array
print_data(c_arr); // ✅ C 数组
print_data(vec); // ✅ vector
print_data(arr); // ✅ std::array
return 0;
}
Running result:
size = 5: 1 2 3 4 5
size = 3: 10 20 30
size = 4: 100 200 300 400
8.2.Example 2: Based on Example 1, a subview of span (subspan)
#include <iostream>
#include <span>
#include <vector>
int main()
{
std::vector<int> vec = {10, 20, 30, 40, 50, 60};
std::span<int> sp(vec);
// 取中间 3 个元素(从索引 1 开始,取 3 个)
auto mid = sp.subspan(1, 3);
std::cout << "mid: ";
for (int n : mid) std::cout << n << " ";
std::cout << "\n";
// 取前 2 个
auto first2 = sp.first(2);
std::cout << "first 2: ";
for (int n : first2) std::cout << n << " ";
std::cout << "\n";
// 取后 3 个
auto last3 = sp.last(3);
std::cout << "last 3: ";
for (int n : last3) std::cout << n << " ";
std::cout << "\n";
// 子视图修改会影响原数据!
mid[0] = 999;
std::cout << "after modifying mid[0] = 999\n";
std::cout << "original vec[1] = " << vec[1] << "\n";
return 0;
}
Running result:
mid: 20 30 40
first 2: 10 20
last 3: 40 50 60
after modifying mid[0] = 999
original vec[1] = 999
8.3.Example 3: Based on Example 2, write a function to compute the sum
#include <iostream>
#include <span>
#include <vector>
// 计算 span 中所有元素的和
double sum(std::span<const double> data)
{
double total = 0.0;
for (double x : data)
{
total += x;
}
return total;
}
// 计算 span 中所有元素的平均值
double average(std::span<const double> data)
{
if (data.empty()) return 0.0;
return sum(data) / data.size();
}
int main()
{
std::vector<double> scores = {85.5, 92.0, 78.5, 90.0, 88.0};
double c_arr[] = {1.1, 2.2, 3.3};
std::cout << "sum(scores) = " << sum(scores) << "\n";
std::cout << "avg(scores) = " << average(scores) << "\n";
std::cout << "sum(c_arr) = " << sum(c_arr) << "\n";
std::cout << "avg(c_arr) = " << average(c_arr) << "\n";
return 0;
}
Running result:
sum(scores) = 434
avg(scores) = 86.8
sum(c_arr) = 6.6
avg(c_arr) = 2.2
9.Operation result
See the "run results" for each example above.
10.Key syntax explanation in the example
| Example | What was discussed | Newly introduced syntax | Why it is written this way | Notes |
|---|---|---|---|---|
| Example 1 | Uniformly accept different containers | std::span<const int>As a parameter |
A single function handles vector/array/C arrays | span is only a view, it does not own data |
| Example 2 | Subview operations | subspan()、first()、last() |
Zero-copy slicing, can locally process large arrays | Subview modifications affect the original data |
| Example 3 | Use span to write generic functions | sum(std::span<const double>) |
Generic algorithms + arbitrary contiguous containers | Inside the function, use range-for and .size() |
10.1.Example 4: Read-only span and writable span
#include <iostream>
#include <span>
#include <vector>
void print(std::span<const int> data)
{
for (int n : data)
{
std::cout << n << " ";
}
std::cout << "\n";
}
void add_offset(std::span<int> data, int offset)
{
for (int& n : data)
{
n += offset;
}
}
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
// vector 是动态数组,元素数量可以在运行时变化。
std::vector<int> values = {10, 20, 30};
print(values);
add_offset(values, 5);
print(values);
return 0;
}
Running result:
10 20 30
15 25 35
print read-only, so use span<const int>;add_offset needs to be modified in place, so use span<int>. This is more general than passing vector<int>& because it can also accept array and C arrays.
11.Common Errors
Error 1: The span outlives the data it references
std::span<int> make_span()
{
std::vector<int> v = {1, 2, 3};
return std::span<int>(v); // ❌ v 是局部变量,span 成为悬挂视图!
}
The correct approach: a span is only a view, so you must ensure the underlying data outlives the span.
Mistake 2: Receiving temporary objects with span
std::span<const int> sp = std::vector<int>{1, 2, 3}; // ❌ 临时对象已销毁!
The correct approach: first create a named container, then pass it to the span.
Error 3: Assuming span can be used with non-contiguous containers
std::list<int> lst = {1, 2, 3};
std::span<int> sp(lst); // ❌ list 不连续!
Correct approach: span is only applicable to contiguous memory containers (vector, array, C arrays, and some scenarios of deque).
12.Usage Recommendations
- Prefer using
std::span<const T>for function parameters instead ofconst vector<T>&; it is more general. - span is only a view:it does not own the data, so you must ensure the underlying data remains alive.
- Subview zero-copy:
subspanand similar operations are very efficient. - C++20 only has span:if your project uses C++17, you can use
std::string_view(a string version of span) or a third-party library. - Prefer read-only parameters
span<const T>:this way vector, array, and C arrays can all be passed, and the function will not modify them.
13.Summary
std::span<T>is a contiguous view of memory that does not own the data.- It can uniformly accept vector, array, and C arrays—one parameter type covers all.
- Supports subview slicing (
subspan、first、last), with zero copy. - The lifetime of a span cannot exceed the underlying data.
- It only applies to contiguous memory containers.