C++20 Span 简介

C++20 引入了 std::span 类模板,它是一个轻量级的、非拥有的范围视图,用于表示连续的内存区域。std::span 可以用于数组、std::vectorstd::string 等数据结构,提供了一种统一的方式来访问这些数据结构的元素。

std::span 首先不是一个容器,而是一个视图,它不拥有数据,只是对数据的引用。这使得 std::span 更加高效,因为它不需要复制数据。std::span 可以用于函数参数,允许函数接受任意大小的数组或容器,而无需复制数据。

本文将介绍 std::span 的基本用法和一些示例。

创建 std::span

std::span 可以通过多种方式创建.在声明时指定了元素数量的是固定 span,未指定大小的是动态 span。动态span的大小可以随着对应容器大小的变化而变化,而固定span的大小是固定的。

std::vector<int> v{1, 2, 3, 4, 5};
// 从vector容器创建 span
auto s1 = std::span{v};
fmt::println("s1: {}", s1);  // s1: [1, 2, 3, 4, 5]

// 使用指针和大小创建 span
auto s2 = std::span{v.data(), 3};
fmt::println("s2: {}", s2);  // s2: [1, 2, 3]

// 从view创建 span
auto s3 = std::views::counted(v.begin() + 2, 3);
fmt::println("s3: {}", s3);  // s3: [3, 4, 5]

// 从数组创建 span
int arr[] = {1, 2, 3, 4, 5};
auto s4 = std::span{arr + 1, 3};
fmt::println("s4: {}", s4);  // s4: [2, 3, 4]

// 从std::array创建 span
std::array<int, 5> arr2{1, 2, 3, 4, 5};
auto s5 = std::span{arr2};
fmt::println("s5: {}", s5);  // s5: [1, 2, 3, 4, 5]

// 从span构建sub span
auto s6 = s5.subspan(1, 3);
fmt::println("s6: {}", s6);  // s6: [2, 3, 4]

std::span 的操作

可以使用 firstlast 方法获取 span 的前几个或后几个元素; 这里有两个版本:

  1. first<N>()last<N>() 返回一个新的 span,包含前 N 个或后 N 个元素。是一个模板函数.
  2. first(N)last(N) 返回一个新的 span,包含前 N 个或后 N 个元素。
  3. subspan(pos, count) 返回一个新的 span,包含从位置 pos 开始的 count 个元素。
std::vector<int> vec{1, 2, 3, 4, 5, 6};
std::span s{vec};

auto sp1 = s.first<2>();
fmt::println("sp1: {}", sp1);  // sp1: [1, 2]

auto sp2 = s.last<2>();
fmt::println("sp2: {}", sp2);  // sp2: [5, 6]

auto sp3 = s.first(2);
fmt::println("sp3: {}", sp3);  // sp3: [1, 2]

auto sp4 = s.last(2);
fmt::println("sp4: {}", sp4);  // sp4: [5, 6]

auto sp5 = s.subspan(2, 3);
fmt::println("sp5: {}", sp5);  // sp5: [3, 4, 5]

std::span 注意事项

  1. std::span 不拥有数据,只是对数据的引用,因此在 std::span 存在时,数据必须保持有效。
  2. std::span 如果span的底层数据被销毁,那么span将变成悬垂指针,这是一种未定义行为。
std::vector<int> vec{1, 2, 3, 4, 5, 6};
std::span s{vec};

auto current = vec.capacity();
// may cause dangling reference
for (auto i = 7; i < 10; i++) {
    vec.push_back(i);
}

if (current != vec.capacity()) {
    // fmt::println("s: {}", s); // Error
    // s now is a dangling reference
    // s should be re-assigned
    s = vec;
}

总结

可以看出std::spanstd::views有很多相似之处, 实际上, std::spanstd::views的一个特例. 关于std::views的更多内容可以查看我之前的博客: C++20 Ranges 简介.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值