【C++】std::span の使い方 | 配列やvectorの一部を安全に参照する方法

目次

はじめに

C++で、関数に配列の一部を渡したい場合、従来はポインタとサイズをペアで渡す必要があり、バグが発生しやすいという問題がありました。

C++20で導入された std::span は、この問題をエレガントに解決します。spanは、連続したメモリ領域への「ビュー」または「参照」であり、それ自体はデータを所有しません。元のデータコンテナ(std::vectorやC言語スタイルの配列など)の一部または全部を、まるでスライスしたかのように安全に、かつ効率的に指し示すことができます。

この記事では、std::span の基本的な使い方と、それがいかにしてC++のコードをより安全で表現力豊かにするのかを解説します。


【前提】C++20とは?

C++20(シーピープラスにーぜろ)は、2020年に正式化されたC++言語のメジャーアップデート版です。std::span はこのC++20で追加された主要機能のため、利用するにはC++20に対応したコンパイラが必要です。


std::span を使ったサンプルコード

このコードは、std::vector を元に span を作成し、その span の一部をさらに subspan として切り出して、それぞれの内容を表示する関数に渡します。

完成コード

#include <iostream>
#include <vector>
#include <span> // std::span を使うために必要

using namespace std;

// span<int> を引数として受け取る関数
void print_span(span<int> s) {
    cout << "[ ";
    for (int value : s) {
        cout << value << " ";
    }
    cout << "]" << endl;
}

int main() {
    vector<int> data = {10, 20, 30, 40, 50, 60};
    
    // 1. vector全体からspanを作成
    cout << "vector全体: ";
    span<int> all_items(data);
    print_span(all_items);
    
    // 2. vectorの一部からspanを作成 (ポインタとサイズを指定)
    cout << "2番目の要素から3つ分: ";
    span<int> partial_items(data.data() + 1, 3); // data[1]から3つ分
    print_span(partial_items);

    // 3. 既存のspanから、さらに一部分を切り出す (subspan)
    cout << "上記spanの、さらに1番目の要素から2つ分: ";
    // partial_itemsは {20, 30, 40} を指している
    // その1番目(30)から2つ分なので {30, 40} になる
    span<int> sub_items = partial_items.subspan(1, 2);
    print_span(sub_items);

    return 0;
}

実行結果

vector全体: [ 10 20 30 40 50 60 ]
2番目の要素から3つ分: [ 20 30 40 ]
上記spanの、さらに1番目の要素から2つ分: [ 30 40 ]

コードの解説

void print_span(span<int> s)

print_span関数は、引数として span<int> を受け取ります。この関数は、引数が std::vector なのか、C言語スタイルの配列なのか、std::array なのかを気にする必要がありません。メモリ上で連続している整数のシーケンスであれば、どんなものでも受け取ることができます。これが span の強力な点です。

span<int> all_items(data);

std::vectorstd::array のようなコンテナから、span を直接作成できます。この spandata の全要素を指し示します。

span<int> partial_items(data.data() + 1, 3);

ポインタとサイズを指定して span を作成することもできます。

  • data.data() + 1: vector の先頭要素へのポインタに 1 を足すことで、2番目の要素のアドレスを指します。
  • 3: そこから3つ分の要素を範囲とします。

span<int> sub_items = partial_items.subspan(1, 2);

.subspan(オフセット, 要素数) は、既存の span から、さらに一部分を切り出した新しい span を作成します。元の spanオフセット 番目から、要素数 個分の要素を指すビューを返します。この操作も非常に高速です。


span を使うメリット

  • 効率性: span は、ポインタとサイズ情報しか持たない軽量なオブジェクトです。span をコピーしても、元のデータのコピーは一切発生しません。
  • 安全性: ポインタとサイズがペアになっているため、範囲外へのアクセスといった、C言語スタイルのポインタ演算にありがちなバグを防ぎやすくなります。
  • 柔軟性: span を使うことで、vectorarray など、異なる種類のコンテナを統一的なインターフェースで扱う関数を記述できます。

まとめ

今回は、C++20の std::span を使って、連続したデータ範囲への安全で効率的なビューを作成する方法を解説しました。span は、従来のポインタとサイズを個別に渡すコーディングスタイルを置き換える、現代C++の重要なツールです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次