web-dev-qa-db-ja.com

vector :: at vs. vector :: operator []

at()は、 C++ Vector at/[] operator speed または :: std ::などの同様の質問で説明されている境界チェックのため、[]よりも遅いことを知っています。 vector :: at()vs operator [] <<驚くべき結果!! 5〜10倍遅い/速い!at()メソッドが何に適しているのか理解できません。

このような単純なベクトルがある場合:std::vector<int> v(10);そして、インデックスiを持っている状況で[]の代わりにat()を使用してその要素にアクセスすることにしますそのベクトルの境界がわからない場合、try-catchブロックでラップするように強制されます

try
{
    v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
    ...
}

size()を使用し、自分でインデックスをチェックすることで同じ動作をすることができますが、これは私にとってより簡単で非常に便利です:

if (i < v.size())
    v[i] = 2;

だから私の質問は:
vector :: operator よりも vector :: at を使用する利点は何ですか? []
いつ vector :: size + vector :: operator ではなく vector :: at を使用すべきですか? []

76
LihO

vector::at()がスローする例外は、すぐに周囲のコードにキャッチされることを意図していないと思います。主に、コードのバグをキャッチするのに役立ちます。実行時に境界チェックする必要がある場合インデックスはユーザー入力から取得されるため、ifステートメントを使用することをお勧めします。要約すると、vector::at()が例外をスローしないようにコードを設計します。そうすると、例外がスローされ、プログラムが異常終了した場合、それはバグの兆候です。 (assert()のように)

59
pmdj

try-catchブロックでラップする必要があります

いいえ、ありません(try/catchブロックはアップストリームにすることができます)。プログラムが未定義の動作領域に入るのではなく、例外をスローする場合に役立ちます。

ベクトルへのほとんどの範囲外アクセスはプログラマーのミスであることに同意します(この場合、assertを使用してこれらのミスをより簡単に特定する必要があります。標準ライブラリのほとんどのデバッグバージョンはこれを自動的に行います)。プログラマーの間違いを報告するためにアップストリームに飲み込むことができる例外を使用したくない:バグを修正できるようにしたい。

ベクトルへの境界外アクセスが通常のプログラムフローの一部である可能性は低いため(そうである場合、あなたは正しいです:例外をバブルアップさせる代わりにsizeで事前に確認してください)、私はあなたの診断に同意します:atは本質的に役に立ちません。

16
Alexandre C.

Vector :: atator []よりもvector :: atを使用する利点は何ですか? vector :: size + vector :: operator []ではなくvector :: atを使用する場合

ここで重要な点は、例外により、通常のコードフローとエラー処理ロジックを分離できることです。また、1つのcatchブロックで、関数呼び出しの内部に散らばっていても、無数のスローサイトから発生する問題を処理できます。したがって、at()が1回の使用で必ずしも簡単になるわけではありませんが、検証するインデックスがたくさんある場合は、簡単になり、通常のロジックがわかりにくくなります。

また、いくつかのタイプのコードでは、インデックスが複雑な方法でインクリメントされ、配列の検索に継続的に使用されることも注目に値します。このような場合、at()を使用して正しいチェックを保証する方がはるかに簡単です。

実際の例として、C++を字句要素にトークン化するコードがあり、次にトークンのベクトル上でインデックスを移動する他のコードがあります。発生した内容に応じて、次のように次の要素をインクリメントしてチェックしたい場合があります。

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

この種の状況では、不適切にが入力の最後に到達したかどうかを確認するのは非常に困難です。これは、検出された正確なトークンに大きく依存しているためです。各使用ポイントでの明示的なチェックは苦痛であり、事前/事後の増分、使用ポイントでのオフセット、以前のテストの有効性の継続に関する欠陥のある推論など、プログラマーエラーの余地があります。

11
Tony Delroy

ベクトルへのポインタがある場合、atはより明確になります。

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

パフォーマンスは別として、これらの最初のものはよりシンプルで明確なコードです。

7
Brangdon

まず、at()または_operator[]_が遅いかどうかは指定されていません。境界エラーがない場合は、少なくともデバッグビルドではほぼ同じ速度になると予想されます。違いは、at()は境界エラー(例外)が発生した場合に何が起こるかを正確に指定することです。ここで_operator[]_の場合のように、未定義の動作です。少なくとも通常のデバッグフラグが使用されている場合は、私が使用するシステム(g ++およびVC++)。 (別の違いは、コードを確認したら、デバッグをオフにすることで_operator[]_の速度を大幅に向上できることです。パフォーマンスが必要な場合は、必要でない限り実行しません。 )

実際には、at()はめったに適切ではありません。コンテキストがインデックスが無効である可能性があることがわかっている場合は、おそらく明示的なテスト(デフォルト値などを返すなど)が必要であり、無効にできないことがわかっている場合は中止します(そして無効かどうかわからない場合は、関数のインターフェイスをより正確に指定することをお勧めします)。ただし、いくつかの例外があります。ユーザーデータの解析により無効なインデックスが発生する可能性があり、エラーによりリクエスト全体が中止されます(ただし、サーバーはダウンしません)。そのような場合、例外が適切であり、at()がそれを行います。

5
James Kanze

例外を使用することの全体的なポイントは、エラー処理コードをさらに遠ざけることができるということです。

この特定のケースでは、ユーザー入力は確かに良い例です。 _std::vector_に内部的に保存する何らかの種類のリソースを参照するためにインデックスを使用するXMLデータ構造を意味的に分析したいと想像してください。 XMLツリーはツリーになったため、おそらく再帰を使用して分析したいと思うでしょう。深く、再帰では、XMLファイルの作成者によるアクセス違反がある可能性があります。その場合、通常はすべての再帰レベルから抜け出し、ファイル全体(またはあらゆる種類の「粗い」構造)を拒否するだけです。これはatが便利な場所です。ファイルが有効であるかのように、分析コードを記述することができます。ライブラリコードがエラー検出を処理し、大まかなレベルでエラーをキャッチできます。

また、_std::map_のような他のコンテナにも_std::map::at_があり、_std::map::operator[]_とは少し異なるセマンティクスを持ちます:atはconstマップで使用できますが、_operator[]_は使用できません。ここで、_const std::vector<T>&_または_const std::map<std::size_t, T>&_のいずれかを処理できるもののように、コンテナーに依存しないコードを作成する場合は、_ContainerType::at_を選択します。

ただし、これらのすべてのケースは通常、何らかの種類の未検証のデータ入力を処理するときに表示されます。通常そうであるように、有効範囲について確信がある場合は、通常_operator[]_を使用できますが、begin()およびend()を使用したイテレーターを使用することもできます。

1
ltjax

this 記事によると、パフォーマンスは別にして、atまたはoperator[]を使用しても違いはありません。ただし、アクセスがベクター。それ以外の場合、アクセスがベクトルの容量にのみ基づいている場合は、atを使用する方が安全です。

0
ahj

注:一部の新しい人々は、何が間違っているかを言うことなく、この答えを否定しています。以下の答えは正解であり、確認できます here

違いは実際には1つだけです。atは境界チェックを行い、operator[]しません。これは、リリースビルドだけでなくデバッグビルドにも適用され、これは標準で非常によく指定されています。とても簡単です。

これにより、atの処理速度が低下しますが、atを使用しないことも非常に悪いアドバイスです。相対的な数字ではなく、絶対的な数字を見る必要があります。私はあなたのコードのほとんどがatよりも脂肪の多い高価な操作をしていると確信しています。個人的には、atを使用しようとします。これは、厄介なバグが未定義の動作を作成して本番環境に忍び込んで欲しくないためです。

0
Shital Shah