std :: stringのc_str()にはどのようなパフォーマンスが期待できますか?常に一定の時間ですか?


13

私は最近、いくつかの必要な最適化を行ってきました。私がやっていることの1つは、ostringstreams-> sprintfsを変更することです。私はたくさんのstd :: stringsをacスタイルの配列、alaにスプリントしています

char foo[500];
sprintf(foo, "%s+%s", str1.c_str(), str2.c_str());

Microsoftのstd :: string :: c_str()実装は一定の時間で実行されることがわかりました(内部ポインターを返すだけです)。と思われるのlibstdc ++が同じことを行います。stdはc_strを保証しませんが、これを行う別の方法を想像するのは難しいと思います。たとえば、メモリにコピーした場合、バッファにメモリを割り当てる必要があります(呼び出し元に任せて破棄します-STLコントラクトの一部ではありません)または、内部staticにコピーする必要がありますバッファ(おそらくスレッドセーフではないため、その寿命については保証されません)。したがって、内部的に維持されているヌル終了文字列へのポインタを単に返すことが唯一の現実的な解決策のようです。

回答:


9

思い出すと、この規格は次のstring::c_str()条件を満たすものをほとんどすべて返すことを許可しています。

  • 文字列および終端の内容に対して十分な大きさのストレージ NULL
  • 指定されたstringオブジェクトの非constメンバーが呼び出されるまで有効でなければなりません

したがって、実際には、これは内部ストレージへのポインターを意味します。返されたポインターの寿命を外部で追跡する方法がないためです。あなたの最適化は、これが(小さな)一定時間であると仮定しても安全だと思います。

関連する注意事項として、文字列のフォーマットがパフォーマンスを制限している場合、Boost.Phoenixのようなもので絶対に必要になるまで評価を延期する方が良いかもしれません。

Boost.Format 結果が必要になるまで内部で書式設定を延期し、書式文字列を再解析せずに同じ書式オブジェクトを繰り返し使用できると考えています。これは、高頻度ロギングで大きな違いをもたらすことがわかりました。


2
実装によって、新しいまたはセカンダリの内部バッファを作成することができます-ヌルターミネータを追加するのに十分な大きさです。たとえc_strCONST方法である(又は少なくともconstの過負荷を有する-私は忘れて)、これは論理値を変更しないので、理由であってもよいですmutable。それはですから、ポインタを破るのコールにc_strどのようにポインタが同じ論理文字列を参照しなければならないことを除いて、(その再配分する新たな理由はありません-すでにヌルターミネータがなければならない)、または他のすでに非への呼び出しが行われている必要があります-constメソッドの中間。
Steve314

これが本当に有効な場合、c_str呼び出しは再割り当てとコピーのためにO(n)時間になる可能性があります。しかし、これを防ぐために私が知らない追加のルールが標準に存在する可能性もあります。私はそれを示唆する理由-呼び出しには、c_str彼らが速いかどうかを確認するために重要と考えることはできませんので、実際に、一般的な私の知る限りであることを意味していないが-で、通常は不要ヌルターミネータ用のストレージの余分なバイトという避けstringインスタンスは使用しないことをc_str5月に優先されました。
Steve314

Boost.Format内部的にはストリームを通過しますが、内部的にsprintfはかなり大きなオーバーヘッドが発生します。ドキュメンテーションは、それが普通より約8倍遅いと述べていsprintfます。パフォーマンスと型安全性が必要な場合は、を試してくださいBoost.Spirit.Karma
Jan Hudec

Boost.Spirit.Karmaはパフォーマンスの良いヒントですが、既存のprintfスタイルコード(およびコーダー)を適応させるのが難しい、非常に異なる方法論があることに注意してください。Boost.Format私のI / Oは非同期であるため、大部分が行き詰まっています。しかし、大きな要因は、同僚に一貫して使用するよう説得できることです(それでも、ostream<<過負荷のある型はすべて許可します- .c_str()議論をうまく回避します)。
右辺値

23

c ++ 11標準(私はN 3290バージョンを読んでいます)では、21.4.7.1章でc_str()メソッドについて説明しています:

const charT* c_str() const noexcept; const charT* data() const noexcept;

戻り値:[0、size()]の各iに対してp + i ==&operatorとなるようなポインターp。
複雑さ:一定の時間。
要件:プログラムは、文字配列に格納されている値を変更してはなりません。

そう、はい:一定時間の複雑さは標準によって保証されています。

私はちょうどc ++ 03標準をチェックしましたが、そのような要件はなく、複雑さもわかりません。


8

理論的には、C ++ 03はそれを必要としません。したがって、文字列は、c_str()が呼び出されたときにヌルターミネータの存在が追加されるcharの配列にすることができます。これには再割り当てが必要な場合があります(内部プライベートポインターがとして宣言されている場合、const-nessに違反しませんmutable)。

C ++ 11はより厳密です。時間のコストがかかるため、再配置を行うことはできず、配列は常に最後にnullを格納するのに十分な幅でなければなりません。c_str()自体ptr[size()]='\0'は、ヌルが実際に存在することを保証するために" "を実行できます。範囲[0..size())は変更されないため、配列の定数に違反しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.