導入
皆さんは、関数の引数に配列やvectorを渡す際、どのような型を使っていますか?これまでは、ポインタとサイズを別々に渡したり、特定のコンテナ型に依存させたりして、汎用性の低さに悩むことがあったはずです。C++20で導入されたstd::spanは、連続するメモリ領域を指す「ビュー(View)」として機能し、これらの課題を解決します。所有権を持たないためオーバーヘッドが極めて少なく、関数引数の標準的な選択肢として非常に強力です。
基礎知識
std::spanとは、一言で言えば「ポインタとサイズを保持する軽量な構造体」です。内部的には、先頭要素へのポインタと、その要素数を管理しています。最大の特徴は、所有権を持たないという点です。メモリの確保や解放は行わず、あくまで「既存のメモリ領域を覗き見るための窓口」として振る舞います。これにより、std::vectorやstd::array、あるいは生配列であっても、同じインターフェースで安全に扱うことが可能になります。
実装/解決策
std::spanを使用するには、ヘッダファイル
サンプルプログラム
以下のコードは、std::spanを使って様々なコンテナを同一の関数で処理する例です。
include
include
include
include
// std::spanを受け取る関数。vectorでも配列でも同じように扱える
void print_elements(std::span
for (const auto& val : s) {
std::cout << val << " ";
}
std::cout << std::endl;
}
int main() {
std::vector
std::array
int raw_array[] = {7, 8, 9};
// それぞれ異なるコンテナだが、span経由で同じ関数に渡せる
print_elements(vec); // vectorを渡す
print_elements(arr); // arrayを渡す
print_elements(raw_array); // 生配列を渡す
return 0;
}
応用・注意点
現場で使う際に最も注意すべき点は、寿命管理です。std::spanは所有権を持たないため、参照先のコンテナが破棄された後でspanにアクセスすると、未定義動作(ダングリングポインタ)を引き起こします。関数内で一時的にデータを処理する目的で使う分には非常に安全ですが、クラスのメンバ変数として保持する際は、参照先の寿命がspanよりも長いことを必ず保証してください。また、std::spanは「連続したメモリ」を前提としているため、std::listのような非連続なコンテナには使用できない点にも注意しましょう。

コメント