はじめに
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::vector
や std::array
のようなコンテナから、span
を直接作成できます。この span
は data
の全要素を指し示します。
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
を使うことで、vector
やarray
など、異なる種類のコンテナを統一的なインターフェースで扱う関数を記述できます。
まとめ
今回は、C++20の std::span
を使って、連続したデータ範囲への安全で効率的なビューを作成する方法を解説しました。span
は、従来のポインタとサイズを個別に渡すコーディングスタイルを置き換える、現代C++の重要なツールです。