最終更新日時: 2025年05月30日 22時19分32秒 (JST)
yoh が更新

履歴 編集

class template
<span>

# std::span(C++20)

    namespace std {
      template <class ElementType, size_t Extent = dynamic_extent>
      class span;

      // viewコンセプトを有効化する
      template<class ElementType, size_t Extent>
      inline constexpr bool ranges::enable_view<span<ElementType, Extent>> = true;

      // borrowed_rangeコンセプトを有効化する
      template<class ElementType, size_t Extent>
      inline constexpr bool ranges::enable_borrowed_range<span<ElementType, Extent>> = true;
    }

## 概要

`std::span`は、シーケンスの所有権を保持せず、部分シーケンスを参照するクラスである。

このクラスは、`std::vector`や配列といったコンテナから一部の連続的な要素を抽出し、それらの要素にのみなんらかの処理を適用する、という目的に使用できる。

文字列操作に特化したクラスとして`std::basic_string_view`が定義されているが、こちらはメモリ連続性をもつあらゆるコンテナに適用できる。

このクラスは、トリビアルコピー可能である（C++23）

### メモリ連続性

このクラスの対象は、メモリの連続性を持つシーケンスである。例として、以下は対象のシーケンスである：

- 組み込み配列
- 要素を指すポインタと要素数の組
- 先頭要素を指すポインタと、末尾要素の次を指すポインタの組
- `std::array`
- `std::vector`

メモリ連続性をもつクラスは、非メンバ関数`data()`によってポインタを取得でき、非メンバ関数`size()`によって要素数を取得できること。それらの関数は、ADLによって呼び出される。

### 静的な要素数と、動的な要素数

`std::span`は、静的な要素数をもつ場合と、動的な要素数をもつ場合の両方をサポートする。それはテンプレートパラメータ`Extent`によって表される。動的な要素数をもつ場合は、`Extent`として`std::dynamic_extent`を指定する。動的な要素数は、`std::vector`を参照したり、ポインタと要素数の組を扱ったり、参照範囲を動的に変更したりする場合に必要となる。

静的な要素数をもつ場合、メンバ定数`extent`に要素数が保持されるため、メンバ変数として要素数を保持する必要がなく、領域を節約する最適化を行える。

### テンプレートパラメータ制約

- `ElementType`は、抽象型ではない完全オブジェクト型であること

### 備考

- `std::span`が参照する範囲`[s.data(), s.data() + s.size())`に含まれるポインタを無効にする操作を行った場合、`std::span`オブジェクトのメンバ関数から返されるその範囲のポインタ、イテレータ、`*this`以外の参照が無効になる

## メンバ関数

### 構築・破棄

名前 | 説明 | 対応バージョン
--- | --- | ---
`(constructor)` | コンストラクタ | C++20
`~span() = default;` | デストラクタ | C++20
`span& operator=(const span&) = default;` `span& operator=(span&&) = default;` | 代入演算子 | C++20

### サブシーケンスの参照

名前 | 説明 | 対応バージョン
--- | --- | ---
`first` | 先頭N個の要素を参照する`span`オブジェクトを取得する | C++20
`last` | 末尾N個の要素を参照する`span`オブジェクトを取得する | C++20
`subspan` | 任意の位置からN個の要素を参照する`span`オブジェクトを取得する | C++20

### 領域

名前 | 説明 | 対応バージョン
--- | --- | ---
`size` | 参照している要素数を取得する | C++20
`size_bytes` | 参照している範囲のバイト数を取得する | C++20
`empty` | 参照している範囲が空かどうかを判定する | C++20

### 要素アクセス

名前 | 説明 | 対応バージョン
--- | --- | ---
`operator[]` | 参照範囲から、任意の位置の要素を取得する | C++20
`at` | 参照範囲から、任意の位置の要素を取得する | C++26
`front` | 参照範囲の先頭要素を取得する | C++20
`back` | 参照範囲の末尾要素を取得する | C++20
`data` | 参照範囲の先頭を指すポインタを取得する | C++20

### イテレータ

名前 | 説明 | 対応バージョン
--- | --- | ---
`begin` | 先頭要素を指すイテレータを取得する | C++20
`end` | 末尾要素の次を指すイテレータを取得する | C++20
`rbegin` | 末尾要素を指す逆順イテレータを取得する | C++20
`rend` | 先頭要素の前を指す逆順イテレータを取得する | C++20
`cbegin` | 先頭の要素を指す読み取り専用イテレータを取得する | C++23
`cend` | 末尾の次を指す読み取り専用イテレータを取得する | C++23
`crbegin` | 末尾を指す読み取り専用逆イテレータを取得する | C++23
`crend` | 先頭の前を指す読み取り専用逆イテレータを取得する | C++23

### メンバ定数

名前 | 説明 | 対応バージョン
--- | --- | ---
`static constexpr size_type extent = Extent;` | 要素数。値が`-1`であれば動的な要素数、そうでなければ静的な固定要素数 | C++20

### メンバ型

名前 | 説明 | 対応バージョン
--- | --- | ---
`element_type` | 要素型 `ElementType` | C++20
`value_type` | CV修飾を除いた要素型 `remove_cv_t<ElementType>` | C++20
`size_type` | インデックスを表す符号なし整数型 `size_t` | C++20
`difference_type` | イテレータの差を表す符号付き整数型 `ptrdiff_t` | C++20
`pointer` | ポインタ型 `element_type*` | C++20
`const_pointer` | `const`ポインタ型 `const element_type*` | C++20
`reference` | 参照型 `element_type&` | C++20
`const_reference` | `const`参照型 `const element_type&` | C++20
`iterator` | 実装定義のイテレータ型。`contiguous_iterator`、`random_access_iterator`、constexprイテレータのモデルであり、コンテナのイテレータに対するすべての要件を満たす | C++20
`reverse_iterator` | 逆順イテレータ `reverse_iterator<iterator>` | C++20
`const_iterator` | 読み取り専用イテレータ `std::const_iterator<iterator>` | C++23
`const_reverse_iterator` | 読み取り専用逆イテレータ `std::const_iterator<reverse_iterator>` | C++23

## 非メンバ関数

### オブジェクトのバイト表現

名前 | 説明 | 対応バージョン
--- | --- | ---
`as_bytes` | 読み取り専用バイト列としてシーケンスを参照する | C++20
`as_writable_bytes` | 書込み可能なバイト列としてシーケンスを参照する | C++20

## 推論補助

名前 | 説明 | 対応バージョン
--- | --- | ---
`(deduction_guide)` | クラステンプレートの推論補助 | C++20

## 例

### 基本的な使い方

    #include <iostream>
    #include <span>
    #include <vector>
    #include <utility>

    // メモリ連続性をもつあらゆる範囲を出力する関数。
    // std::spanオブジェクトはコピーで受け取るのが基本的な使い方
    template <class T, std::size_t Extent>
    void print(std::span<T, Extent> s)
    {
      const char* delimiter = "";

      std::cout << '{';
      for (const T& x : s) {
        std::cout << std::exchange(delimiter, ",") << x;
      }
      std::cout << '}' << std::endl;
    }

    int main()
    {
      std::vector<int> v = {1, 2, 3, 4, 5};
      int ar[] = {1, 2, 3, 4, 5};

      // spanに変換してコンテナ全体を出力
      print(std::span{v});

      // コンテナの一部の要素を出力
      print(std::span{v}.subspan(1, 3));

      // ポインタと要素数を指定した範囲を参照して、
      // 範囲for文を使用する
      print(std::span<int>{ar, 3});
    }

#### 出力

    {1,2,3,4,5}
    {2,3,4}
    {1,2,3}

### データのヘッダ情報とボディ情報をコピーなしで分割する

    #include <iostream>
    #include <span>
    #include <vector>
    #include <utility>

    template <class T>
    void process_header(std::span<T> s)
    {
      const char* delimiter = "";

      std::cout << "[header] : ";
      for (int x : s) {
        std::cout << std::exchange(delimiter, ",") << x;
      }
      std::cout << std::endl;
    }

    template <class T>
    void process_body(std::span<T> s)
    {
      const char* delimiter = "";

      std::cout << "[body] : ";
      for (int x : s) {
        std::cout << std::exchange(delimiter, ",") << x;
      }
      std::cout << std::endl;
    }

    template <class T>
    void f(std::span<T> s)
    {
      std::size_t header_size = 3;
      process_header(s.first(header_size));
      process_body(s.last(s.size() - header_size));
    }

    int main()
    {
      std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      f(std::span{data});
    }

#### 出力

    [header] : 1,2,3
    [body] : 4,5,6,7,8,9,10

## バージョン

### 言語

- C++20

### 処理系

- Clang: 9.0 ✅
- GCC: 10.1 ✅
- Visual C++: 2019 Update 6 ✅

## 参照

- P0122R7 `<span>` (n4740でwording changeあり)
  - N4740 2018年03月 Jacksonville会議
- P1085R2 Should `span` be regular?
- P1227R2 Signed `ssize()` functions, unsigned `size()` functions
- P1024R3 Usability enhancements for `std::span`
- LWG Issue 3102 Clarify `span iterator` and `const_iterator` behavior
- LWG Issue 3144. `span` does not have a `const_pointer` typedef
- LWG Issue 3203. `span` element access invalidation
- P1872R0 `span` should have `size_type`, not `index_type`
- P2051R0 C++ Standard Library Issues to be moved in Prague
  - `const_iterator`, `const_reverse_iterator`, `cbegin()`, `cend()`, `crbegin()`, `crend()`を削除
- P2116R0 Remove tuple-like protocol support from fixed-extent `span`
- P0896R4 The One Ranges Proposal (was Merging the Ranges TS)
- P2325R3 Views should not be required to be default constructible
- Require `span` & `basic_string_view` to be Trivially Copyable
  - C++23から、トリビアルコピー可能が保証される。
- P2278R4 `cbegin` should always return a constant iterator