web-dev-qa-db-ja.com

C ++ 11以降では、std :: string :: operator []は境界チェックを行いますか?

_std::string::operator[]_が境界チェックを行わないことを何度も見てきました。 string :: atとstring :: operator []? の違いは何ですか?2013年に尋ねられた回答によると、_operator[]_は境界チェックを行いません。

これに関する私の問題は、[string.access]の標準(この場合は ドラフトN3797 )を見ると、

_const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
_
  1. 必要なもの:pos <= size()
  2. 戻り値:*(begin() + pos) if pos < size()。それ以外の場合は、値charT()のタイプcharTのオブジェクトへの参照を返します。オブジェクトを変更すると、未定義の動作が発生します。
  3. スロー:何もありません。
  4. 複雑さ:一定時間。

これにより、_operator[]_は、文字列の要素を返す必要があるのか​​、デフォルトのcharTを返す必要があるのか​​を判断するために、何らかの境界チェックを行う必要があると思います。この仮定は正しく、境界チェックを行うには_operator[]_が必要になりましたか?

27
NathanOliver

言葉遣いは少し紛らわしいですが、詳細に調べると、実際には非常に正確であることがわかります。

それはこれを言います:

  • 前提条件は、_[]_の引数が=nであるか、<n
  • 前提条件が満たされていると仮定します:
    • <nの場合、要求した文字を取得します。
    • 「それ以外の場合」(つまり、nの場合)、charT()(つまり、ヌル文字)を取得します。

ただし、前提条件を破った場合のルールは定義されておらず、=nのチェックは暗黙的に満たすことができます(ただし、明示的には満たされません) charT()を実際に位置nに格納することにより、強制されます。

したがって、実装は境界チェックを実行する必要はありません…そして一般的なものは実行しません。

_operator[]_は、ある種の境界チェックを行って決定しました...

いいえ、そうではありません。前提条件付き

必要なもの:pos <= size()。

[〜#〜]仮定[〜#〜]常に文字列の要素を返すことができると仮定できます。この条件が満たされない場合:未定義の動作。

_operator[]_は、文字列の先頭からposだけポインタをインクリメントする可能性があります。文字列が短い場合は、文字列の背後にあるデータへの参照が返されます。単純なC配列の古典的な範囲外のように。

pos == size()の場合を完全にするために、内部文字列データの最後に追加のcharTを割り当てることができます。したがって、チェックなしでポインタをインクリメントするだけでも、指定された動作が提供されます。

14
Superlokkus

まず、requires句があります。 require句に違反すると、プログラムは未定義の方法で動作します。それはpos <= size()です。

したがって、言語はその場合に何が起こるかを定義するだけです。

次の段落では、pos < size()の場合、文字列内の要素への参照を返すと述べています。また、pos == size()の場合、値charT()でデフォルトで作成されたcharTへの参照を返します。

これは境界チェックのように見えるかもしれませんが、実際に起こることは、_std::basic_string_が要求よりも1つ大きいバッファを割り当て、最後のエントリにcharT()を入力することです。次に、_[]_は単にポインタ演算を行います。

私はその実装を回避する方法を考え出そうとしました。規格はそれを義務付けていませんが、私は自分自身に代替案が存在することを納得させることができませんでした。 .data()には、単一のバッファーを回避するのが難しい厄介な問題がありました。

この標準コンテナの演算子は、通常の配列の演算子[]の動作をエミュレートします。したがって、チェックは行いません。ただし、デバッグモードでは、対応するライブラリがこのチェックを提供できます。

インデックスを確認したい場合は、代わりにメンバー関数at()を使用してください。

2

http://en.cppreference.com/w/cpp/string/basic_string/operator_at

指定された位置posにある文字への参照を返します。 境界チェックは実行されません。

(エンファシスマイン)。

境界チェックが必要な場合は、 std :: basic_string :: at を使用します。

この標準は、チェックされていない配列アクセスが行うことを基本的に説明しているため、実装が境界チェックを提供する必要があることを意味します。

範囲内でアクセスする場合、それは定義されています。外に出ると、未定義の動作がトリガーされます。

1
PSkocik