第 18.16 節

std::span

0瀏覽次數0訪問次數--跳出率--平均停留

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:

  1. Passing const std::vector<T>&: Can only receive a vector, not an array or C array.
  2. In the const T* data, size_t size protocol: 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

UsageExplanation
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.


ExampleDiscusses whatNewly emerged syntaxWhy write it this wayPrecautions
Example 1Unified reception of different containers.std::span<const int> as a parameterA function that processes vector/array/C array.A span is just a view and does not own data.
Example 2Subview Operationssubspan()first()last()Zero-copy slicing for processing large arrays locally.Subview modification affects original data
Example 3Write 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 methodYes, it can be modified. Please provide more details.Common uses
std::span<const T>cannot be modifiedRead-only processing, statistics, printing, and verification
std::span<T>can be modifiedFill 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).

使用建议

  • 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
  • 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
  • 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
  • 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
  • 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。

如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。

  1. For function parameters, prefer using std::span<const T>: it's more generic than const vector<T>&.
  2. Span is just a view: It doesn't own the data and must ensure the underlying data survives.
  3. Sub-view zero-copy: subspan and similar operations are very efficient.
  4. 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.
  5. 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.
音乐页