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


237

最近span<T>、コードでを使用するよう提案するか、サイトでを使用するいくつかの回答を見ましたspan。ある種のコンテナだと思われます。しかし、私はC ++ 17標準ライブラリでそのようなものを見つけることができません。

では、この不可解なspan<T>とは何か、そしてそれが非標準である場合、なぜ(またはいつ)使用するのが良いのでしょうか?


std::span2017年に提案されました。C++ 17またはC ++ 20に適用されます。P0122R5、スパン:オブジェクトのシーケンスの境界セーフビューも参照してください。その言語をターゲットに設定しますか?コンパイラが追いつくまでには何年もかかるでしょう。
JWW

6
@jww:スパンはC ++ 11でかなり使用可能です... gsl::spanではなくstd::span。以下の私の回答も参照してください。
einpoklum 2018年


1
@KeithThompson:2017年ではなかった...
einpoklum

@jwwすべてのコンパイラがstd :: span <>をC ++ 20モードでサポートするようになりました。そして、スパンは多くのサードパーティのライブラリから入手できます。あなたは正しかった-それは年でした:正確には2年。
コンタンゴ

回答:


272

それは何ですか?

A span<T>は:

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

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

いつ使用すべきですか?

まず、それを使用しない場合:

  • ちょうどのような、スタート&終了イテレータの任意のペアを取ることができるコードでは使用しないでください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
  • (スパンを使用している)コードが、ポイントされたメモリを所有していないことを示します。

spans を使用する動機はさらにあります。これはC ++コアガイドラインで見つけることができますが、ドリフトをキャッチします。

標準ライブラリにないのはなぜですか(C ++ 17以降)?

これは標準ライブラリにありますが、C ++ 20以降のみです。その理由は、C ++コアガイドラインプロジェクトとの関連で考案された現在の形式では、まだかなり新しいからです。これは、2015年以来、形になっているだけです(コメンターが指摘するように、これには以前の履歴があります)。

それが標準ライブラリにまだない場合、どうすれば使用できますか?

これは、コアガイドラインのサポートライブラリ(GSL)の一部です。実装:

  • Microsoft / Neil MacintoshのGSLには、スタンドアロンの実装が含まれています。gsl/span
  • GSL-Liteは、GSL全体のシングルヘッダー実装です(それほど大きくありません。心配しないでください)span<T>

GSLの実装では、通常、C ++ 14サポートを実装するプラットフォームを想定しています[ 14 ]。これらの代替シングルヘッダー実装は、GSL機能に依存しません。

これらの異なるスパンの実装では、付属するメソッド/サポート関数にいくつかの違いがあることに注意してください。また、C ++ 20の標準ライブラリに入るバージョンとは多少異なる場合があります。


深い読み:あなたがC ++ 17の前に、最終的な公式提案ですべての詳細および設計上の考慮事項を見つけることができ、P0122R7:スパン:オブジェクトのシーケンスの境界に安全な景色ニールMacintoshとステファンJ. Lavavejによります。少し長いですが。また、C ++ 20では、スパン比較セマンティクスが変更されました(Tony van Eerdによるこの短い論文に続いて)。


2
一般的な範囲(イテレータ+センチネルとイテレータ+長さをサポートし、イテレータ+センチネル+長さをサポートする可能性があります)を標準化し、スパンを単純なtypedefにすることは、より理にかなっています。なぜなら、それはより一般的だからです。
デュプリケータ

3
@Deduplicator:範囲はC ++で提供される予定ですが、現在の提案(Eric Nieblerによる)ではConceptsのサポートが必要です。したがって、C ++ 20の前ではありません。
einpoklum 2017

8
@HảiPhạmLê:配列はすぐにポインターに減衰しません。やってみるstd::cout << sizeof(buffer) << '\n'と、100のsizeof(int)が得られることがわかります。
einpoklum 2017

4
@Jim std::arrayはコンテナであり、値を所有しています。spanは所有されてい
ません

3
@ジム:std::array完全に別の獣です。その長さはコンパイル時に固定され、Calethが説明したように、参照型ではなく値型です。
einpoklum 2018

1

@einpoklumは、ここで彼の回答に a spanが何であるかを紹介するのにかなり良い仕事をしています。ただし、彼の回答を読んだ後も、スパンに不慣れな人が、次のように、完全には回答されていない一連の思考の質問をまだ持っていることは簡単です。

  1. spanC配列とどう違うのですか?それらの1つだけを使用しないのはなぜですか?サイズもわかっているものの1つにすぎないようです...
  2. 待って、それはのように聞こえstd::arrayますが、それとどうspan違うのですか?
  3. ああ、私を連想させるものではないstd::vectorようにstd::array、あまりにも?
  4. 私は困惑している。:(は何spanですか?

それで、それについていくつかの追加の明快さがあります:

彼の答えの直接の引用- 私の追加が太字になっています:

それは何ですか?

A span<T>は:

  • Tメモリ内のどこかにある型の値の連続したシーケンスの非常に軽量な抽象化。
  • 基本的に、便利なメソッドの束を持つ単一の構造体{ T * ptr; std::size_t length; }(注意これは明らかに異なっているstd::array<>ためspanに匹敵する方法、アクセッサ利便性を可能にstd::array介して、型へのポインタTタイプおよび長さ(要素の数)Tに対し、std::array一つ以上の保持実際のコンテナである型は、T。)
  • 非所有型(つまり、「値型」ではなく「参照型」):何も割り当てたり割り当て解除したりすることはなく、スマートポインターを存続させません。

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

これらの太字の部分は理解するために重要ですので、見落としたり、読み違えたりしないでください。A spanは構造体のC配列ではなく、タイプのC配列に配列のT長さを加えた構造体でもありません(これは基本的にstd::array コンテナーと同じです)、NORはポインターの構造体のC配列です入力するTプラス長さではなく、それは、単一の単一含む構造体のタイプへのポインタをT、そして長さであり、(タイプの要素数Tポインタを入力することを連続したメモリブロック内)Tにポイントを!このように、使用して追加した唯一のオーバーヘッドは、spanは、ポインタと長さを格納する変数、およびがspan提供する使用する便利なアクセサ関数です。

これはUNLIKE a std::array<>です。これはstd::array<>、が隣接するブロック全体に実際にメモリを割り当てるstd::vector<>ためです。また、std::vector基本的にはa std::arrayがいっぱいになるたびに動的に増加する(通常はサイズが2倍になる)だけなので、UNLIKEになります。 。A std::arrayはサイズが固定されており、a spanはポイントするブロックのメモリを管理することもせず、メモリブロックをポイントするだけで、メモリブロックの長さを知り、C配列内のデータ型を知っています。メモリ内にあり、その連続したメモリ内の要素を操作するための便利なアクセサ関数を提供します

これ C ++標準の一部です。

std::spanC ++ 20以降のC ++標準の一部です。そのドキュメントはhttps://en.cppreference.com/w/cpp/container/spanで読むことができます今日のabsl::Span<T>(array, length) C ++ 11以降でのGoogleの使用方法については、以下を参照してください。

概要の説明と主な参考資料:

  1. std::span<T, Extent>Extent= "シーケンス内の要素の数、またはstd::dynamic_extent動的の場合。"スパンメモリを指すだけでアクセスを容易にしますが、管理はしません!):
    1. https://en.cppreference.com/w/cpp/container/span
  2. std::array<T, N>(サイズが固定されていることに注意してNください!):
    1. https://en.cppreference.com/w/cpp/container/array
    2. http://www.cplusplus.com/reference/array/array/
  3. std::vector<T> (必要に応じて自動的に動的にサイズが大きくなります):
    1. https://en.cppreference.com/w/cpp/container/vector
    2. http://www.cplusplus.com/reference/vector/vector/

今日span、C ++ 11以降でどのように使用できますか?

グーグルは、自社の「Abseil」ライブラリの形で内部C ++ 11ライブラリをオープンソース化しています。このライブラリは、C ++ 14からC ++ 20以降の機能をC ++ 11以降で機能するように提供することを目的としているため、今日の明日の機能を使用できます。彼らが言うには:

C ++標準との互換性

Googleは、C ++ 14、C ++ 17、およびそれ以降に組み込まれている機能に一致またはほぼ一致する多くの抽象化を開発しました。これらの抽象化のAbseilバージョンを使用すると、コードがポストC ++ 11の世界でまだ準備ができていない場合でも、これらの機能に今すぐアクセスできます。

主なリソースとリンクは次のとおりです。

  1. メインサイト:https : //abseil.io/
  2. https://abseil.io/docs/cpp/
  3. GitHubリポジトリ:https : //github.com/abseil/abseil-cpp
  4. span.hヘッダー、およびabsl::Span<T>(array, length)テンプレートクラス:https : //github.com/abseil/abseil-cpp/blob/master/absl/types/span.h#L189

1
重要で役立つ情報を教えてくれたと思います。
グイリマ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.