C++ 11の後、私はc_str()
とdata()
を考えました equivalently 。
C++ 17は後者にオーバーロードを導入し、非定数ポインタを返します( reference 、C++ 17で完全に更新されているかどうかはわかりません):
const CharT* data() const; (1)
CharT* data(); (2) (since C++17)
c_str()
は定数ポインターのみを返します:
const CharT* c_str() const;
特にC++ 11がそれらを同種にするものであったときに、C++ 17でこれら2つのメソッドを区別するのはなぜですか?言い換えると、なぜ一方のメソッドだけがオーバーロードを取得し、他方はオーバーロードを取得しなかったのでしょうか?
新しいオーバーロードは、C++ 17の P0272R1 によって追加されました。論文自体もその中のリンクも、data
のみに新しいオーバーロードが与えられ、c_str
には与えられなかった理由については説明していません。この時点では推測しかできません(ディスカッションチャイムに関係する人がいない限り)が、検討のために以下のポイントを提供したいと思います。
オーバーロードをdata
に追加しただけでも、コードが破損しました。この変更を控えめに保つことは、悪影響を最小限に抑える方法でした。
c_str
関数は、これまでdata
と完全に同一であり、「C文字列」、つまりimmutable、ヌル終了文字配列。いつでもc_str
をdata
に置き換えることができるため、このレガシーインターフェイスに追加する特別な理由はありません。
P0292R1の動機は、誤って、またはCの理由で、変更されていなくても変更可能なポインターのみを使用するレガシーAPIが存在することでした。それでも、文字列の既に大規模なAPIに絶対に必要なものを追加したくないと思います。
もう1つのポイント:C++ 17の時点で、あなたはare今 allowed towrite 値ゼロを書き込む限り、nullターミネータに。 (以前は、ヌルターミネータに何かを書き込むためにUBを使用していました。)可変のc_str
は、この特定の微妙さへのさらに別のエントリポイントを作成します。
data()
メンバーが過負荷になった理由は、open-std.orgの this paperで説明されています。
TL;論文のDR:std::string
の非const .data()
メンバー関数が標準の均一性を改善するために追加されましたライブラリとC++開発者が正しいコードを書くのを支援します。 C文字列パラメーターにconst修飾を持たないCライブラリ関数を呼び出すときにも便利です。
論文からの関連する一節:
要約
std::string
の非const.data()
メンバー関数の欠如は、C++ 11より前のstd::string
セマンティクスに基づいた監視または意図的な設計ですか?どちらの場合でも、この機能の欠如により、開発者はいくつかの正当なシナリオで安全でない代替物を使用するようになります。このペーパーでは、標準ライブラリの均一性を改善し、C++開発者が正しいコードを書くのを支援するために、std :: stringに非const.data()
メンバー関数を追加することを主張します。ユースケース
Cライブラリには、char *パラメーターを持つルーチンが含まれることがあります。 1つの例は、Windows APIのlpCommandLine
関数のCreateProcess
パラメーターです。std::string
のdata()
メンバーはconstであるため、std :: stringオブジェクトをlpCommandLine
パラメーターと連動させるために使用することはできません。開発者は、次の例のように、代わりに.front()
を使用する傾向があります。std::string programName; // ... if( CreateProcess( NULL, &programName.front(), /* etc. */ ) ) { // etc. } else { // handle error }
programName
が空の場合、programName.front()
式は未定義の動作を引き起こすことに注意してください。一時的な空のC文字列がバグを修正します。std::string programName; // ... if( !programName.empty() ) { char emptyString[] = {'\0'}; if( CreateProcess( NULL, programName.empty() ? emptyString : &programName.front(), /* etc. */ ) ) { // etc. } else { // handle error } }
std::vector
のように非const.data()
メンバーがあった場合、正しいコードは簡単です。std::string programName; // ... if( !programName.empty() ) { char emptyString[] = {'\0'}; if( CreateProcess( NULL, programName.data(), /* etc. */ ) ) { // etc. } else { // handle error } }
非const
.data() std::string
メンバー関数は、C文字列パラメーターにconst修飾がないCライブラリ関数を呼び出すときにも便利です。これは、古いコードや古いCコンパイラで移植性が必要なコードで一般的です。
それは「あなたがそれで何をしたいのか」のセマンティクスに依存します。一般的に、std::string
は、バッファベクトルとして、つまりstd::vector<char>
の代わりとして使用されることがあります。これは、boost::asio
でよく見られます。つまり、文字の配列です。
c_str()
:厳密には、ヌル終了文字列を探していることを意味します。その意味で、データを変更したり、非定数として文字列を使用することは決してしないでください。
data()
:文字列内の情報がバッファデータとして、さらには非定数としても必要になる場合があります。文字列の長さの変更を伴わない限り、データを変更する必要がある場合としない場合があります。
Std :: stringクラスの履歴により、std :: stringの2つのメンバー関数c_strおよびdataが存在します。
C++ 11までは、std :: stringはコピーオンライトとして実装できました。内部表現は、格納された文字列のヌル終了を必要としませんでした。メンバー関数c_strは、返された文字列がnullで終了することを確認しました。メンバー関数data simlpyは、格納された文字列へのポインタを返しましたが、これは必ずしもヌル文字で終了していませんでした。 -コピーオンライトを有効にするために文字列への変更が確実に通知されるように、両方の関数はconstデータへのポインターを返す必要がありました。
これはすべて、コピーオンライトがstd :: stringで許可されなくなったときにC++ 11で変更されました。 c_strはnullで終了する文字列を配信するために必要であったため、実際の保存された文字列には常にnullが追加されます。それ以外の場合、c_strを呼び出すと、格納されたデータを変更して、文字列をヌルで終了させる必要があり、これによりc_strが非const関数になります。 dataは保存された文字列へのポインタを配信するため、通常はc_strと同じ実装を持ちます。下位互換性のため、両方の機能が引き続き存在します。