Skip to content

Wiki

std::span

About 8 min read

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:

  1. Pass const std::vector<T>&: Can only receive vector, cannot receive array or C array.
  2. 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

  1. Prefer using std::span<const T> for function parameters instead of const vector<T>&; it is more general.
  2. span is only a view:it does not own the data, so you must ensure the underlying data remains alive.
  3. Subview zero-copy:subspan and similar operations are very efficient.
  4. 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.
  5. 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.