在 C++20 里,std::span 被引入作为一个轻量级的、无所有权的容器视图(view),它允许你以安全、直观且高效的方式访问数组、std::vector、std::array 或任意连续存储的内存块。相比传统的指针加长度的方式,std::span 通过统一的接口和编译期检查提供了更高的可读性与安全性,同时在性能上几乎不引入额外开销。
1. std::span 的基本定义
template<class ElementType, std::size_t Extent = std::dynamic_extent>
class span {
public:
using element_type = ElementType;
using value_type = std::remove_cv_t <ElementType>;
using size_type = std::size_t;
using difference_type= std::ptrdiff_t;
using pointer = ElementType*;
using const_pointer = const ElementType*;
using reference = ElementType&;
using const_reference= const ElementType&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator <iterator>;
using const_reverse_iterator = std::reverse_iterator <const_iterator>;
// 构造
constexpr span() noexcept;
constexpr span(pointer ptr, size_type count);
template<class ArrayType, std::size_t N>
constexpr span(ArrayType(&)[N]) noexcept;
template<class Container, class = std::enable_if_t<
std::is_convertible_v<
decltype(std::declval <Container>().data()), pointer>>>
constexpr span(Container& c) noexcept;
// … 其它成员函数
};
Extent为静态尺寸,如果不指定则为动态(std::dynamic_extent)。span并不拥有数据,只是对已有数据的视图。
2. 为什么 std::span 更快?
2.1 无额外开销
span 实际上只是两个成员:pointer 与 size_type。编译器会把它视为 POD(Plain Old Data),在函数调用时通常被直接放入寄存器(如 x86_64 的 RDI/RSI),与传统的 T* + size_t 参数几乎相同。
2.2 编译期尺寸检查
当 Extent 为常量时,编译器能检查数组大小,避免越界。例如:
constexpr std::array<int, 10> arr{0};
std::span<int, 10> sp(arr); // OK
std::span<int, 5> sp2(arr); // 编译错误,尺寸不匹配
这在调试阶段能提前发现错误,减少运行时检查。
2.3 与 STL 容器无缝交互
许多 STL 算法已接受 std::span 作为参数,甚至可以直接对 `std::vector