web-dev-qa-db-ja.com

std :: vectorのイテレータのインデックスを取得するための最も効果的な方法は何ですか?

ベクトルを反復処理しているので、反復子が現在指しているインデックスが必要です。これは2つの方法で実行できます。

  • it - vec.begin()
  • std::distance(vec.begin(), it)

これらの方法の長所と短所は何ですか?

391
cairol

Naveenとは反対の理由で、it - vec.begin()を正確に使用することをお勧めします。したがって、ベクトルをリストに変更した場合、はコンパイルされません。すべての繰り返しでこれを実行すると、O(n)アルゴリズムをO(n ^ 2)アルゴリズムに簡単に変換できます。

もう1つの選択肢は、反復中にコンテナ内でジャンプしない場合は、インデックスを2番目のループカウンタとして保持することです。

注:itは、コンテナー・イテレーターの一般名、std::container_type::iterator it;です。

495
UncleBens

コードを変更しなくてもコンテナを変更できるようになるので、std::distance(vec.begin(), it)をお勧めします。たとえば、ランダムアクセスイテレータを提供していないstd::listの代わりにstd::vectorを使用することにした場合、コードはコンパイルされます。 std :: distanceはイテレータの特性に応じて最適な方法を選択するので、パフォーマンスの低下もありません。

125
Naveen

UncleBensとNaveenが示しているように、両方に良い理由があります。どちらが「より良い」かは、どのような振る舞いがしたいかによって異なります。一定時間の振る舞いを保証したいのか、それとも必要に応じて線形時間にフォールバックしたいのか

it - vec.begin()は一定の時間がかかりますが、operator -はランダムアクセスイテレータでしか定義されていないので、例えばコードはリストイテレータではまったくコンパイルされません。

std::distance(vec.begin(), it)はすべてのイテレータ型で機能しますが、ランダムアクセスイテレータで使用された場合にのみ定時操作になります。

どちらも「より良い」というわけではありません。あなたが必要とすることをするものを使いなさい。

71
jalf

私はこれが好きです:it - vec.begin()、私にとってそれは明らかに「最初からの距離」を言っているので。イテレータでは、算術の観点から考えることに慣れているので、ここでは-記号が最も明確な指標です。

11
Eli Bendersky

あなたのアルゴリズムが既にstd::vector::iteratorstd::vector::iteratorのみを使うように制限/ハードコードされているなら、どちらの方法を使うかは重要ではありません。あなたのアルゴリズムは、他のものを選ぶことがどんな違いをも生み出すことができるという点を超えてすでに具体化されています。両方ともまったく同じことをします。それは個人的な好みの問題です。私は個人的には明示的減算を使用します。

その一方で、あなたがあなたのアルゴリズムでより高い程度の一般性を保持したいなら、すなわち将来いつかそれが他のイテレータ型に適用されるかもしれないという可能性を可能にするために、最良の方法はあなたの意図に依存します。これは、ここで使用できるイテレータ型に関してどれだけ制限があるかによって異なります。

  • 明示的な減算を使用する場合、アルゴリズムはかなり狭いクラスの反復子、つまりランダムアクセス反復子に制限されます。 (これはstd::vectorから得られるものです)

  • distanceを使う場合、あなたのアルゴリズムはもっと広いクラスのイテレータをサポートするでしょう:入力イテレータ。

もちろん、非ランダムアクセスイテレータのdistanceの計算は一般的に非効率的な演算です(一方、ランダムアクセスのイテレータの場合も減算と同じくらい効率的です)。あなたのアルゴリズムが非ランダムアクセスイテレータには意味があるかどうか、効率的に決めるのはあなた次第です。結果として生じる効率の低下は、アルゴリズムを完全に役に立たなくするという点で壊滅的なものになります。その場合は減算に固執する必要があります。非ランダムアクセスイテレータの効率がまだ使用可能な範囲内にある場合は、distanceを使用して、アルゴリズムがランダムアクセスイテレータのほうがうまく機能するという事実を文書化する必要があります。

7
AnT

http://www.cplusplus.com/reference/std/iterator/distance/ によると、vec.begin()ですランダムアクセスイテレータでは、distanceメソッドは-演算子を使用します。

そのため、パフォーマンスの観点からは答えは同じですが、distance()を使用する方が、コードを読んで理解しなければならない場合は理解しやすいでしょう。

4
Stéphane

私は-のためだけにstd::vectorバリアントを使用したいと思います - それが意味するところはかなり明らかです、そして操作の単純さ(これはポインタ減算以上のものではありません)は構文(distance側、最初の読書でピタゴラスのように聞こえますね。 UncleBenが指摘するように、vectorが誤ってlistに変更された場合、-は静的アサーションとしても機能します。

また、私はそれがはるかに一般的であると思います - しかし、それを証明する数字はありません。マスター引数:it - vec.begin()はソースコードが短い - タイピング作業が少なく、スペースが消費されません。あなたの質問に対する正しい答えが好みの問題であることが明らかになっているので、これはも有効な議論である可能性があります。

3