web-dev-qa-db-ja.com

C ++ 11で標準ライブラリコンテナを効率的に選択するにはどうすればよいですか?

「C++ Container choice」と呼ばれるよく知られた画像(チートシート)があります。これは、使用目的に最適なコンテナを選択するためのフローチャートです。

すでにC++ 11バージョンが存在するかどうかを知っていますか?

これは前のものです: eC++ Container choice

130
BlakBat

私が知っているわけではありませんが、テキストで行うことができます。また、listは一般的にそれほど良いコンテナではなく、forward_list。両方のリストは、ニッチアプリケーション用の非常に特殊なコンテナです。

このようなチャートを作成するには、2つの簡単なガイドラインが必要です。

  • セマンティクスを最初に選択する
  • いくつかの選択肢がある場合は、最も単純な

通常、パフォーマンスについて心配することは最初は役に立ちません。 Oの大きな考慮事項は、数千(またはそれ以上)のアイテムの処理を開始したときにのみ有効です。

コンテナには2つの大きなカテゴリがあります。

  • アソシエイティブコンテナ:find操作があります
  • シンプルシーケンスコンテナ

そして、それらの上にいくつかのアダプターを構築できます:stackqueuepriority_queue。アダプターはここでは省略しますが、認識できるほど十分に特殊化されています。


質問1:連想的

  • oneキーで簡単に検索する必要がある場合は、連想コンテナが必要です。
  • 要素をソートする必要がある場合は、順序付けられた連想コンテナが必要です。
  • それ以外の場合は、質問2にジャンプします。

質問1.1:Ordered

  • 特定の順序が不要な場合は、unordered_コンテナ、それ以外の場合は従来の順序付けられた対応物を使用します。

質問1.2:Separate Key

  • キーが値とは別の場合はmapを使用し、そうでない場合はsetを使用します

質問1.3:Duplicates

  • 重複を保持する場合は、multiを使用します。それ以外の場合は使用しません。

例:

一意のIDが関連付けられた複数の人物がいて、できるだけ簡単にそのIDから人物データを取得したいとします。

  1. find関数、つまり連想コンテナが必要です

    1.1。私は順序についてあまり気にすることができなかったので、unordered_コンテナ

    1.2。私のキー(ID)は、関連付けられている値とは別であるため、map

    1.3。 IDは一意であるため、重複することはありません。

最後の答えは:std::unordered_map<ID, PersonData>


質問2:安定したメモリ

  • 要素がメモリ内で安定している必要がある場合(つまり、コンテナ自体が変更されたときに要素が移動しないようにする必要がある場合)、listを使用します
  • それ以外の場合は、質問3にジャンプします。

質問2.1:Which

  • list;で解決します。 forward_listは、メモリフットプリントが少ない場合にのみ役立ちます。

質問3:動的サイズ

  • コンテナのサイズが既知の場合(コンパイル時)、andこのサイズはプログラムの実行中に変更されません、and要素はデフォルトで構築可能または完全な初期化リストを提供できます({ ... }構文)、arrayを使用します。従来のC配列を置き換えますが、便利な機能を備えています。
  • それ以外の場合は、質問4にジャンプします。

質問4:Double-ended

  • 前面と背面の両方から項目を削除できるようにする場合は、dequeを使用します。それ以外の場合は、vectorを使用します。

デフォルトでは、連想コンテナが必要でない限り、vectorが選択されることに注意してください。 Sutter and Stroustrupの推奨 であることが判明しました。

93
Matthieu M.

Matthieuの答えは気に入っていますが、フローチャートを次のように言い換えます。

Std :: vectorを使用しない場合

デフォルトでは、もののコンテナが必要な場合は、_std::vector_を使用します。したがって、他のすべてのコンテナーは、_std::vector_の代替機能を提供することによってのみ正当化されます。

コンストラクター

_std::vector_では、アイテムをシャッフルできる必要があるため、そのコンテンツは移動構築可能である必要があります。これは、コンテンツに与えるひどい負担ではありません(デフォルトのコンストラクターは不要ですemplaceなどのおかげです) 。ただし、他のコンテナーのほとんどは特定のコンストラクターを必要としません(これもemplaceのおかげです)。したがって、移動コンストラクタを絶対にcannot実装するオブジェクトがある場合は、別のものを選択する必要があります。

_std::deque_は一般的な置換で、_std::vector_の多くのプロパティを持ちますが、両端キューの両端にしか挿入できません。中央のインサートは移動が必要です。 _std::list_は、その内容に要件を設けません。

Boolsが必要

_std::vector<bool>_は...ではありません。まあ、それは標準です。しかし、通常の意味ではvectorではありません。_std::vector_が通常許可する操作は禁止されているためです。そして、最も確かににはboolsが含まれていません。

したがって、vectorsのコンテナーから実際のbool動作が必要な場合は、_std::vector<bool>_から取得することはできません。したがって、_std::deque<bool>_を支払う必要があります。

検索中

コンテナ内の要素を見つける必要があり、検索タグを単なるインデックスにできない場合は、setおよびmapを優先して_std::vector_を放棄する必要がある場合があります。キーワード「may」に注意してください。ソートされた_std::vector_は、合理的な代替手段である場合があります。または、Boost.Containerの _flat_set/map_ は、ソートされた_std::vector_を実装します。

現在、これらには4つのバリエーションがあり、それぞれにニーズがあります。

  • 検索タグが自分が探しているアイテムと同じでない場合は、mapを使用します。それ以外の場合は、setを使用します。
  • コンテナにアイテムのlotがあり、検索パフォーマンスがO(1)である必要がある場合は、unorderedを使用します。 O(logn)ではなく。
  • 同じ検索タグを持つために複数のアイテムが必要な場合は、multiを使用します。

ご注文

特定の比較操作に基づいて常にアイテムのコンテナをソートする必要がある場合は、setを使用できます。または、同じ値を持つために複数のアイテムが必要な場合は_multi_set_。

または、並べ替えられた_std::vector_を使用できますが、並べ替えておく必要があります。

安定

イテレータと参照が無効化されることが懸念される場合があります。アイテムのリストが必要な場合(他のさまざまな場所にあるアイテムへのイテレーター/ポインターがある場合)、無効化に対する_std::vector_のアプローチは適切ではない可能性があります。現在のサイズと容量によっては、挿入操作によって無効化が発生する場合があります。

_std::list_は確固たる保証を提供します。イテレータとそれに関連する参照/ポインタは、アイテム自体がコンテナから削除されたときにのみ無効になります。 _std::forward_list_は、メモリが深刻な問題である場合に存在します。

それがあまりにも強力な保証である場合、_std::deque_はより弱いが有用な保証を提供します。無効化は中央への挿入から生じますが、先頭または末尾への挿入は、コンテナ内のアイテムへのポインタ/参照ではなく、反復子の無効化のみを引き起こします。

挿入性能

_std::vector_は、最後に安価な挿入のみを提供します(そして、それでも、容量を爆破すると高価になります)。

_std::list_はパフォーマンスの点で高価です(新しく挿入された各アイテムにはメモリ割り当てがかかります)が、consistentですまた、実質的にパフォーマンスコストをかけずにアイテムをシャッフルしたり、パフォーマンスを損なうことなく同じタイプの他の_std::list_コンテナとアイテムを交換したりする場合に不可欠な機能も提供します。シャッフルa lotする必要がある場合は、_std::list_を使用します。

_std::deque_は、先頭と末尾での一定時間の挿入/削除を提供しますが、中央への挿入はかなり高価になる可能性があります。したがって、前面だけでなく背面からものを追加/削除する必要がある場合は、_std::deque_が必要な場合があります。

移動セマンティクスのおかげで、_std::vector_挿入のパフォーマンスは以前ほど悪くないことに注意してください。一部の実装では、ムーブセマンティックベースのアイテムコピー(いわゆる「スワップ化」)の形式を実装しましたが、現在、ムービングは言語の一部であり、標準で義務付けられています。

動的割り当てなし

_std::array_は、できるだけ少ない動的割り当てが必要な場合に適したコンテナです。これは、C配列の単なるラッパーです。つまり、サイズはcompile-timeで認識されている必要があります。それと一緒に暮らすことができる場合は、_std::array_を使用します。

つまり、サイズに_std::vector_とreserveingを使用すると、境界のある_std::vector_に対しても同様に機能します。このように、実際のサイズは変化する可能性があり、1つのメモリ割り当てしか取得できません(容量を使い果たしていない限り)。

48
Nicol Bolas

上記のフローチャートのC++ 11バージョンを以下に示します。 [元の著者に帰属することなく元々投稿された Mikael Persson ]

24
Wasim Thabraze

簡単な手順を示しますが、おそらく作業が必要です

Should the container let you manage the order of the elements?
Yes:
  Will the container contain always exactly the same number of elements? 
  Yes:
    Does the container need a fast move operator?
    Yes: std::vector
    No: std::array
  No:
    Do you absolutely need stable iterators? (be certain!)
    Yes: boost::stable_vector (as a last case fallback, std::list)
    No: 
      Do inserts happen only at the ends?
      Yes: std::deque
      No: std::vector
No: 
  Are keys associated with Values?
  Yes:
    Do the keys need to be sorted?
    Yes: 
      Are there more than one value per key?
      Yes: boost::flat_map (as a last case fallback, std::map)
      No: boost::flat_multimap (as a last case fallback, std::map)
    No:
      Are there more than one value per key?
      Yes: std::unordered_multimap
      No: std::unordered_map
  No:
    Are elements read then removed in a certain order?
    Yes:
      Order is:
      Ordered by element: std::priority_queue
      First in First out: std::queue
      First in Last out: std::stack
      Other: Custom based on std::vector????? 
    No:
      Should the elements be sorted by value?
      Yes: boost::flat_set
      No: std::vector

主にリンクされたノードが好きではないという事実により、これがC++ 03バージョンとwildlyで異なることに気付くかもしれません。いくつかのまれな状況を除き、リンクされたノードコンテナは、通常、リンクされていないコンテナによってパフォーマンスが低下する可能性があります。これらの状況がわからない場合、およびブーストへのアクセス権がある場合は、リンクノードコンテナーを使用しないでください。 (std :: list、std :: slist、std :: map、std :: multimap、std :: set、std :: multiset)。このリストは主に中小規模のコンテナに焦点を当てています。なぜなら、(A)これはコードで扱うものの99.99%であり、(B)多数の要素には異なるコンテナではなくカスタムアルゴリズムが必要だからです。

1
Mooing Duck