25

C++11 では、std::string§ 21.4.1/5 で指摘されているように、a の文字を連続して格納する必要があります。

basic_string オブジェクト内の文字のようなオブジェクトは、連続して格納されます。つまり、任意の basic_string オブジェクト s について、同一性 &*(s.begin() + n) == &*s.begin() + n は、0 <= n < s.size となる n のすべての値に対して保持されます。 ().

ただし、§ 21.4.7.1 で基になるストレージへのポインターを取得する 2 つの関数を一覧表示する方法を次に示します (強調は私のものです)。

const charT* c_str() const noexcept;
const charT* data() const noexcept;
1 戻り値: [0,size()] 内の各 i に対して p + i == &operator[](i) となるポインター p。
2 複雑さ: 一定時間。
3 必須: プログラムは、文字配列に格納されている値を変更してはなりません。

ポイント番号 3 について私が考えることができる 1 つの可能性は、オブジェクトの次の使用によってポインターが無効になる可能性があることです (§ 21.4.1/6)。

  • const 以外の basic_string への参照を引数として取る標準ライブラリ関数への引数として。
  • operator[]、at、front、back、begin、rbegin、end、rend を除く非 const メンバー関数の呼び出し。

それでも、イテレータは無効になる可能性がありますが、無効になるまで変更することはできます。ポインターが無効になるまでは、ポインターを使用してバッファーから読み取ることもできます。

このバッファに直接書き込めないのはなぜですか? end()たとえば、新しい終了で更新されないなど、クラスが矛盾した状態になるためでしょうか? もしそうなら、なぜのようなもののバッファに直接書き込むことが許可されているのstd::vectorですか?

この使用例には、a のバッファーをstd::stringC インターフェイスに渡して、代わりに a を渡す代わりに文字列を取得し、vector<char>そこからの反復子で文字列を初期化できることが含まれます。

std::string text;
text.resize(GetTextLength());
GetText(text.data());
4

1 に答える 1

36

このバッファに直接書き込めないのはなぜですか?

明らかなポイントを述べます。それはconst. そして、値をキャストしてからconstそのデータを変更するのは...失礼です。

では、なぜconstですか?これは、コピー オン ライトが良いアイデアと考えられていた時代にさかのぼり、std::basic_string実装がそれをサポートできるようにする必要がありました。コピーのオーバーヘッドを発生させずに、文字列への不変ポインターを取得することは非常に便利です (たとえば、C-API に渡すため) 。そのため、ポインターc_strを返す必要がありました。const

なぜそれはまだ constですか?まあ...それは標準の奇妙なことになります:ヌルターミネータ。

これは正当なコードです:

std::string stupid;
const char *pointless = stupid.c_str();

pointlessNUL で終わる文字列でなければなりません。具体的には、NUL 文字へのポインタでなければなりません。では、NUL 文字はどこから来たのでしょうか? std::string実装がこれを機能させるには、いくつかの方法があります。

  1. 一般的な手法である小さな文字列の最適化を使用します。このスキームでは、すべてstd::stringの実装に、単一の NUL 文字に使用できる内部バッファーがあります。
  2. NUL 文字を含むstatic memoryへのポインターを返します。したがって、空の文字列の場合、すべてstd::stringの実装で同じポインターが返されます。

すべての人に SSO の実装を強制するべきではありません。そのため、標準化委員会は 2 番目の議題を維持する方法を必要としていました。その一部は、constから文字列を取得することですc_str()。そして、このメモリは本物 constである可能性が高く、偽の「このメモリを変更しないでください」ではないため、constそれへの可変ポインタを与えることは悪い考えです。

もちろん、 を実行することでそのようなポインターを取得することはできます&str[0]が、標準では、NUL ターミネーターを変更することは悪い考えであることは非常に明確です。

そうは言っても、ポインターとその中の文字の配列を変更することは完全に有効です。&str[0]半開きの範囲 [0, str.size()) にとどまる限り。dataまたはによって返されるポインターを介してそれを行うことはできませんc_str。はい、実際には標準が真実である必要 str.c_str() == &str[0]がありますが。

それはあなたにとって標準です。

于 2013-01-12T07:14:32.810 に答える