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


438

ベクトルを反復処理していて、イテレータが現在指しているインデックスが必要です。私の知る限り、これは2つの方法で行うことができます:

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

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

回答:


557

私はit - vec.begin()ナビーンによって与えられた反対の理由で正確に好みます:それでそれはしません、ベクターをリストに変更してコンパイルされ。すべての反復でこれを行うと、O(n)アルゴリズムをO(n ^ 2)アルゴリズムに簡単に変換してしまう可能性があります。

反復中にコンテナー内をジャンプしない場合の別のオプションは、インデックスを2番目のループカウンターとして保持することです。

注:itはコンテナイテレータの一般的な名前ですstd::container_type::iterator it;


3
同意した。マイナス記号が最も良いと思いますが、この関数が遅くなる可能性があるため、std :: distanceを使用するよりも2番目のループカウンターを保持する方が良いでしょう。
Steven Sudit、2010

28
一体何itですか?
Steinfeld、2014

32
@Steinfeldはイテレータです。std::container_type::iterator it;
Matt Munson

2
2番目のループカウンターを追加することは非常に明白な解決策であり、私がそれを考えなかったのは恥ずかしいことです。
Mordred

3
@Swapnil std::listは、位置による要素への直接アクセスを提供しないため、実行できない場合はlist[5]、実行できないはずですlist.begin() + 5
ホセ・トマスTocino

135

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


50
ランダムアクセスイテレータのないコンテナを使用している場合、そのような距離は効率が悪いため計算しないことが最善です
Eli Bendersky

6
@Eli:私はそれに同意しますが、それが本当に必要な非常に特殊なケースでは、そのコードは機能します。
Naveen、2010年

9
コンテナが変更された場合でも、コードは変更する必要があると思います。std:: list変数を指定することvecは悪いニュースです。コンテナータイプをテンプレートパラメーターとして使用して、コードがジェネリックに書き換えられた場合、それは、ランダムアクセス以外の反復子の処理について話すことができる(そしてすべきである)ときです;-)
Steve Jessop

1
そして特定の容器のための専門化。
ScaryAardvark 2010

19
@SteveJessop:名前の付いたベクターを持つことvecもかなり悪いニュースです。
タム川

74

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

it - vec.begin()は一定の時間がかかりoperator -ますが、これはランダムアクセス反復子でのみ定義されるため、たとえば、コードはリスト反復子でコンパイルできません。

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

どちらも「良い」ものではありません。必要なことを行うものを使用してください。


1
私は過去にこれに反したことがあります。2つのstd :: mapイテレータでstd :: distanceを使用し、O(N)であると想定しています。
ScaryAardvark、2010

6
@ScaryAardvark:O(1)になると期待しているのではないですか?
10

12

私はこれが好きですit - vec.begin()。なぜなら、私には「最初からの距離」がはっきりと書かれているからです。イテレータを使うと、私たちは算術の考え方に慣れているので、ここでの-符号は最も明確な指標です。


19
文字通り、単語を使用するよりも、減算を使用して距離を見つける方が明確distanceです。
Travis Gockel 2010年

4
@Travis、私にとってはそうです。それは好みと習慣の問題です。と言ってit++、のようなものstd::increment(it)ではありませんか?それも不明確なものとしてカウントされませんか?
Eli Bendersky、2010年

3
++オペレータは、我々はイテレータをインクリメントする方法としてSTLシーケンスの一部として定義されます。 std::distance最初の要素と最後の要素の間の要素数を計算します。-オペレーターが働いているという事実は単なる偶然です。
Travis Gockel 2010年

3
@MSalters:まだ、++を使用しています:-)
Eli Bendersky

10

アルゴリズムを既に制限/ハードコーディングしている場合は、アルゴリズムを使用しstd::vector::iteratorstd::vector::iteratorだけ、本当にあなたが使用して終了れる方法は重要ではありません。アルゴリズムは、もう一方を選択することで違いが出る可能性がある点を超えてすでに具体化されています。どちらもまったく同じことを行います。それは個人的な好みの問題です。個人的には明示的な減算を使用します。

一方、アルゴリズムの一般性をより高く維持したい場合、つまり、将来的には他のイテレータタイプに適用される可能性を許容する場合、最適な方法は意図に依存します。 。これは、ここで使用できるイテレータタイプに関してどの程度制限したいかに依存します。

  • 明示的な減算を使用する場合、アルゴリズムはかなり狭いクラスのイテレータ、つまりランダムアクセスイテレータに制限されます。(これはあなたが今得たものですstd::vector

  • を使用するdistance場合、アルゴリズムははるかに広いクラスのイテレータ、つまり入力イテレータをサポートします。

もちろん、distance非ランダムアクセス反復子の計算は、一般に非効率的な操作です(ランダムアクセスの場合は、減算と同じくらい効率的です)。アルゴリズム非ランダムアクセス反復子に対して効率的に意味成すかどうかを決めるのはあなた次第です。結果として生じる効率の損失がアルゴリズムを完全に役に立たないものにするという点で壊滅的である場合、減算に固執する必要があります。したがって、非効率的な使用を禁止し、ユーザーに他の反復子タイプの代替ソリューションを探すことを強います。非ランダムアクセス反復子を使用した効率がまだ使用可能な範囲内にある場合はdistance、アルゴリズムがランダムアクセス反復子でより適切に機能するという事実を使用して文書化する必要があります。


4

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

したがって、答えは、パフォーマンスの観点からは同じですがdistance()、誰かがコードを読んで理解する必要がある場合は、使用する方が理解しやすいでしょう。


3

私は-バリアントstd::vectorのみを使用します-意味がかなり明確であり、操作の単純さ(これはポインタの減算にすぎません)は構文で表現されます(distance反対側では、ピタゴラスのように聞こえます)最初に読んでいませんか?)UncleBenが指摘するように、誤って次のように変更さ-れた場合の静的アサーションとしても機能しますvectorlistます。

また、私はそれがはるかに一般的だと思います-しかし、それを証明する数字はありません。マスター引数:it - vec.begin()ソースコードが短い-入力作業が減り、消費されるスペースが減ります。あなたの質問に対する正しい答えが好みの問題であると結論づけられることは明らかであるので、これは有効な議論になることあります。


0

以下は、インデックスとともに10の「すべて」の出現を検索する例です。これはいくつかの助けになると思いました。

void _find_all_test()
{
    vector<int> ints;
    int val;
    while(cin >> val) ints.push_back(val);

    vector<int>::iterator it;
    it = ints.begin();
    int count = ints.size();
    do
    {
        it = find(it,ints.end(), 10);//assuming 10 as search element
        cout << *it << " found at index " << count -(ints.end() - it) << endl;
    }while(++it != ints.end()); 
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.