std::span
What problem does this section solve?
You need to write a function to process a contiguous block of data—which could be a vector, an array, or a C array. Before C++20, you have two options:
- Passing
const std::vector<T>&: Can only receive a vector, not an array or C array. - In the
const T* data, size_t sizeprotocol: it can receive everything, but loses size information, making it prone to writing errors.
std::span provides a unified read-only (or writable) view that doesn't own the data—it acts merely as a "window" that allows uniform handling of all contiguous memory containers.
What is this feature?
std::span<T> is a view type introduced in C++20 that doesn't own data—it's just a lightweight wrapper consisting of a pointer and a size for a contiguous block of memory. You can think of it as a "reference to an array."
C++ standard version
C++20
Required header files
#include <span>
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 个元素的子视图
Common Usage
| Usage | Explanation |
|---|---|
void f(std::span<const int> data) | accepts any contiguous integer container |
std::span<int>(vec) | Create from vector |
std::span<int>(arr) | Create from C array |
sp.subspan(offset, count) | Create subview (no copy) |
Example code
Example 1: Using span to handle 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;
}
Results:
size = 5: 1 2 3 4 5
size = 3: 10 20 30
size = 4: 100 200 300 400
Example 2: Based on Example 1, subviews (subspan) of span
#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;
}
Results:
mid: 20 30 40
first 2: 10 20
last 3: 40 50 60
after modifying mid[0] = 999
original vec[1] = 999
Example 3: Based on Example 2, write a function that calculates 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;
}
Results:
sum(scores) = 434
avg(scores) = 86.8
sum(c_arr) = 6.6
avg(c_arr) = 2.2
runtime results
See the "running results" for each example above.
Key syntax explanation in the example
|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.
| Example | Discusses what | Newly emerged syntax | Why write it this way | Precautions |
|---|---|---|---|---|
| Example 1 | Unified reception of different containers. | std::span<const int> as a parameter | A function that processes vector/array/C array. | A span is just a view and does not own data. |
| Example 2 | Subview Operations | subspan()、first()、last() | Zero-copy slicing for processing large arrays locally. | Subview modification affects original data |
| Example 3 | Write generic functions using span. |
The term "span" typically refers to a non-owning view over a contiguous sequence of objects, commonly used in languages like C++ (std::span) or as a concept in other contexts (like slicing in JavaScript).
Here are examples of how you might write a generic function using the span concept:
1. In C++ (using std::span)
std::span (introduced in C++20) provides a safe, non-owning view over a contiguous range of data. It allows you to write functions that work with arrays, vectors, and other contiguous containers without copying.
#include <span>
#include <iostream>
#include <vector>
// A generic function that calculates the sum of elements in any contiguous sequence
template <typename T>
T sum(std::span<const T> data) {
T total = T{};
for (const auto& value : data) {
total += value;
}
return total;
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
int arr[] = {10, 20, 30};
// The same function works with different containers
std::cout << "Vector sum: " << sum<int>(numbers) << std::endl;
std::cout << "Array sum: " << sum<int>(arr) << std::endl;
// You can also use a sub-span (a slice)
std::cout << "Partial vector sum: " << sum<int>(std::span<const int>(numbers).subspan(1, 3)) << std::endl; // sums elements at index 1, 2, 3
return 0;
}
2. In JavaScript (using array slicing or typed arrays)
JavaScript doesn't have a direct span type, but you can create a generic function that operates on "array-like" objects or uses slicing to mimic span behavior.
// A generic function that works on any iterable (Array, TypedArray, etc.)
function sum(iterable) {
let total = 0;
for (const value of iterable) {
total += value;
}
return total;
}
// Or a function that explicitly takes an array and optional start/end indices (like a span)
function sumRange(array, start = 0, end = array.length) {
let total = 0;
for (let i = start; i < end; i++) {
total += array[i];
}
return total;
}
const arr = [1, 2, 3, 4, 5];
const typedArray = new Uint8Array([10, 20, 30]);
console.log(sum(arr)); // 15
console.log(sum(typedArray)); // 60
console.log(sumRange(arr, 1, 4)); // 2+3+4 = 9
Key Concepts Behind "Span" Functions:
- Non-owning: The function doesn't take ownership or copy the data; it just operates on a view.
- Contiguous memory: It assumes elements are stored sequentially in memory.
- Safe & generic: The function can work with different types (int, float, custom objects) and different container types (arrays, vectors, etc.) as long as the data is contiguous.
If you meant a different type of "span" (e.g., in HTML/CSS, a span element), or if you need an example in a specific language or framework, please provide more details!|sum(std::span<const double>)|general algorithm + arbitrary continuous container|Inside the function, using range-for and .size() is sufficient.|
The difference between span<const T> and span<T>
| writing method | Yes, it can be modified. Please provide more details. | Common uses |
|---|---|---|
std::span<const T> | cannot be modified | Read-only processing, statistics, printing, and verification |
std::span<T> | can be modified | Fill the buffer, normalize the data, in-place filtering |
Doesn't own data, so "whether it's const" is very important. Function parameters default to prioritizing std::span<const T>, only writing std::span<T> when you explicitly want to modify the original data.
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;
}
Results:
10 20 30
15 25 35
print is read-only, so span<const int> is used; add_offset needs in-place modification, so span<int> is used. This is more versatile than passing vector<int>&, since it can also accept array and C arrays.
Common Errors
Error 1: 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: span is merely a view; you must ensure the underlying data outlives the span.
Error 2: Receiving a temporary object with span
std::span<const int> sp = std::vector<int>{1, 2, 3}; // ❌ 临时对象已销毁!
Correct approach: first create the named container, then pass it to span.
Error 3: Believing that span can be used for non-contiguous containers
std::list<int> lst = {1, 2, 3};
std::span<int> sp(lst); // ❌ list 不连续!
Correct practice: span is only suitable for contiguous memory containers (vector, array, C arrays, and some scenarios of deque).
使用建议
- 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
- 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
- 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
- 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
- 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。
如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。
- For function parameters, prefer using
std::span<const T>: it's more generic thanconst vector<T>&. - Span is just a view: It doesn't own the data and must ensure the underlying data survives.
- Sub-view zero-copy:
subspanand similar operations are very efficient. - Only Available in C++20: If your project uses C++17, you can use
std::string_view(a string version of span) or a third-party library. - Read-only parameters take priority
span<const T>: This allows vectors, arrays, and C arrays to be passed, and they won’t be modified by functions.
Summary
std::span<T>is a non-owning contiguous memory view.- Can uniformly handle vector, array, and C array—a single parameter type covers all.
- Supports subview slicing (
subspan,first,last), zero copy. - The lifecycle of a span cannot exceed that of the underlying data.
- Only applicable to continuous memory containers.