**C++20 std::span:实用指南**

在C++20中,std::span被引入作为一个轻量级的、非拥有的数组视图。它在多种场景下极大地简化了代码,提高了可读性和安全性。本文将深入探讨std::span的核心特性、常见使用模式以及如何在已有项目中逐步迁移。


1. 何为 std::span

std::span 只保存了指向连续存储的指针和长度信息,它不拥有底层数据,也不负责内存管理。它的声明方式为:

template< class T, size_t Extent = dynamic_extent >
class span;
  • T:元素类型
  • Extent:大小,若为dynamic_extent(默认)则大小在运行时确定;若为常量,则在编译期固定。

2. 基本使用

2.1 创建

std::vector <int> vec{1,2,3,4,5};
std::span <int> s1(vec);                // 从容器创建
int arr[] = {10,20,30};
std::span <int> s2(arr);                // 从数组创建
std::span <int> s3{vec.data(), 3};       // 指定长度

2.2 访问

for (auto val : s1) std::cout << val << ' ';
std::cout << "\n";
std::cout << s1[2] << '\n';            // 访问
s1[2] = 99;                            // 修改

2.3 子视图

auto sub = s1.subspan(1, 3);           // 从索引1开始,长度3
auto tail = s1.last(2);                // 最后2个元素
auto head = s1.first(2);               // 前2个元素

3. 与容器的协同

3.1 作为参数

void process(std::span<const int> data) {
    for (int v : data) {
        // ...
    }
}

process(vec);    // 直接传递 vector
process(arr);    // 直接传递数组

3.2 与算法配合

std::sort(s1.begin(), s1.end());       // 使用标准算法

3.3 与字符串

std::string str = "Hello, world!";
std::span <char> span_str(str.data(), str.size());
span_str[0] = 'h';                     // 修改原字符串

4. 性能与安全

  • 零成本std::span 仅包含两个指针,编译器可以轻松优化为裸指针。
  • 范围检查span::at 提供边界检查,默认访问不检查。
  • 不可变:使用 std::span<const T> 可以强制只读访问,避免意外修改。

5. 常见误区

  1. 误认为拥有所有权
    span 并不管理内存,必须确保底层数据在使用期间有效。
  2. 对齐与布局
    由于span不复制元素,使用时需保证底层容器连续布局(如std::vector、数组、std::array)。
  3. 不适用于链表
    链表等非连续存储容器不能直接转换为span

6. 逐步迁移示例

假设已有函数 void foo(int* data, size_t n),可改为:

void foo(std::span <int> data) {
    // 现在可以使用更安全的接口
}

在调用点:

int arr[5] = {1,2,3,4,5};
foo(arr);               // 自动匹配
std::vector <int> v = {1,2,3};
foo(v);                 // 也能直接传递

7. 进阶:span 的扩展

  • std::dynamic_extent:使用动态长度时的占位符。
  • std::as_bytesstd::as_writable_bytes:将任意对象转换为字节视图。
  • std::ranges::subrange:在C++23中与span兼容,提供更丰富的范围操作。

8. 结语

std::span 在C++20中以其简洁性与安全性为语言生态注入了新的活力。它不仅能让函数接口更具表达力,也能让代码在保持高性能的同时减少错误。无论是新项目还是维护已有代码,熟练掌握span都是提升代码质量的重要一步。

发表评论