web-dev-qa-db-ja.com

「スパン」とは何ですか。いつ使用する必要がありますか?

最近私は自分のコードでspan<T>を使うことを提案するか、あるいはここでspanを使う答えを見ました - おそらくある種のコンテナです。しかし、C++標準ライブラリにはそのようなものはありません。

それで、この不思議なspan<T>は何ですか、そしてそれが標準的でないならなぜそれを使うのが良い考えですか(そしていつ)

170
einpoklum

それは何ですか?

span<T>は次のとおりです。

  • メモリ内のどこかにあるT型の値の連続したシーケンスを非常に軽量に抽象化したものです。
  • 基本的には便利なメソッドがたくさんあるstruct { T * ptr; size_t length; }
  • 非所有型(つまり、 "値型"ではなく "参照型" ):これは、なにも割り当ても割り当ても解除もせず、スマートポインタを有効にしません。

以前は array_view として知られていましたが、以前は array_ref として知られていました。

いつ使うべきですか?

まず、notがそれを使うとき:

  • std::sortstd::find_ifstd::copy、およびこれらのすべてのスーパージェネリックテンプレート関数のように、任意の開始および終了反復子のペアを受け取ることができるコードでは使用しないでください。
  • 標準のライブラリコンテナ(またはBoostコンテナなど)がある場合は、それを使用しないでください。それはそれらのどれにも取って代わることを意図していません。

それを実際に使うときのために

長さの値がある独立したspan<T>(それぞれspan<const T>)の代わりにT*(それぞれconst T*)を使用します。そのため、次のような関数を置き換えます。

  void read_into(int* buffer, size_t buffer_size);

と:

  void read_into(span<int> buffer);

なぜ私はそれを使うべきですか?なぜそれは良いことですか?

ああ、スパンは素晴らしいです! span...を使用する.

  • あなたが空想の、くっきりとした標準ライブラリコンテナを使うのと同じように、あなたはそのポインタ+長さ/開始+終了ポインタの組み合わせで働くことができることを意味します。

    • for (auto& x : my_span) { /* do stuff */ }
    • std::find_if(my_span.begin(), my_span.end(), some_predicate);

    ...しかし、ほとんどのコンテナクラスで発生するオーバーヘッドはまったくありません。

  • コンパイラがあなたのためにもっと多くの仕事をすることを可能にします。たとえば、

    int buffer[BUFFER_SIZE];
    read_into(buffer, BUFFER_SIZE);
    

    これになります:

    int buffer[BUFFER_SIZE];
    read_into(buffer);
    

    ...あなたがやりたいことをやります。 ガイドラインP.5 もご覧ください。

  • データがメモリ内で連続していることが予想されるときにconst vector<T>&を関数に渡すことに対する合理的な代替方法です。これ以上に強力なC++の達人に叱られることはもうありません。

  • 静的解析を容易にするので、コンパイラはあなたが愚かなバグを見つけるのを助けることができるかもしれません。
  • 実行時の境界チェックのためのデバッグコンパイルインスツルメンテーションを可能にします(つまり、spanのメソッドは、#ifndef NDEBUG ... #endif内にいくつかの境界チェックコードを持ちます)。
  • (スパンを使用している)コードがポインタを所有していないことを示します。

C++のコアガイドライン で見つけることができるspansを使う動機はさらにあります - しかし、あなたはそのドリフトに気付きます。

標準ライブラリにないのはなぜですか(C++ 17現在)。

これは標準ライブラリにありますが、C++ 20以降に限られます。その理由は、現在の形ではまだかなり新しいことであり、2015年以来形作られてきた C++コアガイドライン プロジェクトとの関連で考え出されています。

それではまだ標準ライブラリに入っていないのならどうやって使うの?

これは Core Guidelines のSupport Library(GSL)の一部です。実装:

  • Microsoft/Neil Macintoshの GSL には、スタンドアロンの実装が含まれています。 gsl/span
  • GSL-Lite は、span<T>を含め、GSL全体を単一ファイルで実装したものです(それほど大きくはありません、心配しないでください)。

C++ 17だけではなく、C++ 11およびC++ 14の言語標準の以前のバージョンでも使用できます。


さらに読むこと: C++ 17、P0122R7の前の最後の公式の提案で詳細と設計上の考慮点をすべて見つけることができます: span:オブジェクトシーケンス に対する境界安全なビュー[] Neal MacintoshとStephan J.Lavavej。それは少し長いです。また、C++ 20では、スパン比較セマンティクスが変更されました(Tony van Eerdによる this short paper の後)。

193
einpoklum