# std::span

< cpp‎ | container

定义于头文件 `<span>` |  |
template< class T, std::size_t Extent = std::dynamic_extent > class span; |  | (C++20 起)

类模板 `span` 所描述的对象能指代对象的相接序列，序列的首元素在零位置。 `span` 能拥有静态长度，该情况下序列中的元素数已知并编码于类型中，或拥有动态长度。

典型实现只保有两个成员：指向 `T` 的指针和大小。

`std::span` 的每个特化均为可平凡复制 (TriviallyCopyable) 类型。 | (C++23 起)

### 模板形参

T | - | 元素类型；必须是完整对象类型且非抽象类
Extent | - | 序列中的元素数，或若它为动态则为 `std::dynamic_extent`

### 成员类型

成员类型 | 定义
`element_type` | `T`
`value_type` | std::remove_cv_t<T>
`size_type` | std::size_t
`difference_type` | std::ptrdiff_t
`pointer` | `T*`
`const_pointer` | `const T*`
`reference` | `T&`
`const_reference` | `const T&`
`iterator` | 实现定义的老式随机访问迭代器 (LegacyRandomAccessIterator) 、常量表达式迭代器 (ConstexprIterator) 兼老式连续迭代器 (LegacyContiguousIterator) ，其 `value_type` 为 `value_type`
`reverse_iterator` | std::reverse_iterator<iterator>
`const_reverse_iterator` | std::reverse_iterator<const_iterator>

注意：若 `T` 无 const 限定则 `iterator` 为可变迭代器。

容器 (Container) 的迭代器类型上的所有要求亦应用于 `span` 的 `iterator` 类型。

### 成员常量

static constexpr std::size_t extent = Extent; |  |

### 成员函数

(构造函数) | 构造 `span` (公开成员函数)
operator= | 赋值 `span` (公开成员函数)
##### 迭代器
begin (C++20) | 返回指向起始的迭代器 (公开成员函数)
end (C++20) | 返回指向末尾的迭代器 (公开成员函数)
rbegin (C++20) | 返回指向起始的逆向迭代器 (公开成员函数)
rend (C++20) | 返回指向末尾的逆向迭代器 (公开成员函数)
##### 元素访问
front (C++20) | 访问第一个元素 (公开成员函数)
back (C++20) | 访问最后一个元素 (公开成员函数)
operator[] | 访问序列的元素 (公开成员函数)
data | 返回指向元素序列起始的指针 (公开成员函数)
##### 观察器
size | 返回序列中的元素数 (公开成员函数)
size_bytes | 返回以字节表示的序列大小 (公开成员函数)
empty | 检查序列是否为空 (公开成员函数)
##### 子视图
first | 获得由序列首 N 个元素组成的子段 (公开成员函数)
last | 获得由序列末 N 个元素组成的子段 (公开成员函数)
subspan | 获得子段 (公开成员函数)

### 非成员函数

as_bytes as_writable_bytes | 转换 `span` 为对其底层字节的视图 (函数模板)

### 非成员常量

dynamic_extent (C++20) | `size_t` 类型常量，指明 `span` 拥有动态长度 (常量)

### 辅助模板

template<class T, std::size_t Extent> inline constexpr bool ranges::enable_borrowed_range<std::span<T, Extent>> = true; |  |

`std::ranges::enable_borrowed_range` 的此特化使得 `span` 满足 `borrowed_range` 。

template<class T, std::size_t Extent> inline constexpr bool ranges::enable_view<std::span<T, Extent>> = true; |  |

`std::ranges::enable_view` 的此特化使零或动态长度的 `span` 满足 `view` 。

### 推导指引

### 注解

即使在 C++23 中引入的正式要求前，所有既存实现中 `std::span` 的特化已经为可平凡复制类型。

### 示例

示例用 `std::span` 实现连续范围上的算法。

    #include <algorithm>
    #include <cstddef>
    #include <iostream>
    #include <span>

    template<class T, std::size_t N> [[nodiscard]]
    constexpr auto slide(std::span<T,N> s, std::size_t offset, std::size_t width) {
        return s.subspan(offset, offset + width <= s.size() ? width : 0U);
    }

    template<class T, std::size_t N, std::size_t M> [[nodiscard]]
    constexpr bool starts_with(std::span<T,N> data, std::span<T,M> prefix) {
        return data.size() >= prefix.size()
            && std::equal(prefix.begin(), prefix.end(), data.begin());
    }

    template<class T, std::size_t N, std::size_t M> [[nodiscard]]
    constexpr bool ends_with(std::span<T,N> data, std::span<T,M> suffix) {
        return data.size() >= suffix.size()
            && std::equal(data.end() - suffix.size(), data.end(),
                          suffix.end() - suffix.size());
    }

    template<class T, std::size_t N, std::size_t M> [[nodiscard]]
    constexpr bool contains(std::span<T,N> span, std::span<T,M> sub) {
        return std::search(span.begin(), span.end(), sub.begin(), sub.end()) != span.end();
    //  return std::ranges::search(span, sub).begin() != span.end();
    }

    void print(const auto& seq) {
        for (const auto& elem : seq) std::cout << elem << ' ';
        std::cout << '\n';
    }

    int main()
    {
        constexpr int a[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
        constexpr int b[] { 8, 7, 6 };

        for (std::size_t offset{}; ; ++offset) {
            constexpr std::size_t width{6};
            auto s = slide(std::span{a}, offset, width);
            if (s.empty())
                break;
            print(s);
        }

        static_assert(starts_with(std::span{a}, std::span{a,4})
            && starts_with(std::span{a+1, 4}, std::span{a+1,3})
            && !starts_with(std::span{a}, std::span{b})
            && !starts_with(std::span{a,8}, std::span{a+1,3})
            && ends_with(std::span{a}, std::span{a+6,3})
            && !ends_with(std::span{a}, std::span{a+6,2})
            && contains(std::span{a}, std::span{a+1,4})
            && !contains(std::span{a,8}, std::span{a,9}));
    }

输出：

    0 1 2 3 4 5
    1 2 3 4 5 6
    2 3 4 5 6 7
    3 4 5 6 7 8

### 缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

DR | 应用于 | 出版时的行为 | 正确行为
--- | --- | --- | ---
P2325R3 | C++20 | 非零静态长度的 `span` 不是 `view` | `view` 不要求 `default_initializable` 故它们是

### 参阅

initializer_list (C++11) | 在列表初始化中创建临时数组然后引用它 (类模板)
basic_string_view (C++17) | 只读的字符串视图 (类模板)
ranges::subrange (C++20) | 将迭代器/哨位对结合为一个 `view` (类模板)