`string.assign(string.data()、5)`は明確に定義されていますか、それともUBですか?


11

同僚はこれを書きたかった:

std::string_view strip_whitespace(std::string_view sv);

std::string line = "hello  ";
line = strip_whitespace(line);

戻るstring_viewと私はアプリオリに不安になり、さらにここでのエイリアシングはUBのように見えました。

line = strip_whitespace(line)この場合、と同等であると確信できますline = std::string_view(line.data(), 5)。私はそれが呼び出すと信じてstring::operator=(const T&) [with T=string_view]と同等になるように定義されている、line.assign(const T&) [with T=string_view]と等価になるように定義され、line.assign(line.data(), 5)これを実行するために定義されています:

Preconditions: [s, s + n) is a valid range.
Effects: Replaces the string controlled by *this with a copy of the range [s, s + n).
Returns: *this.

しかし、これはエイリアシングがあるときに何が起こるかについては言いません。

昨日cpplang Slackでこの質問をしたところ、さまざまな答えがありました。ここで非常に信頼できる回答、および/または実際のライブラリベンダーの実装の実証分析を探しています。


私は、テストケースを書いたためにstring::assignvector::assigndeque::assignlist::assign、とforward_list::assign

  • Libc ++は、これらすべてのテストケースを機能させます。
  • Libstdc ++はforward_list、segfaultを実行するを除き、すべてを動作させます。
  • MSVCのライブラリについて知りません。

libstdc ++のsegfaultは、これがUBであることを期待しています。しかし、少なくとも一般的なケースでは、libc ++とlibstdc ++の両方がこれを機能させるために多大な努力をしていることもわかります。


ASanでテストケースをコンパイルしたり、Valgrindで実行したりしましたか?これにより、コードがアクセス違反を引き起こすかどうかを推測する必要がなくなりますが、実際には実際には機能しない可能性があります。
Konrad Rudolph

1
「basic_stringのメンバー関数または演算子が例外をスローした場合、その関数または演算子は、basic_stringオブジェクトに他の影響を与えません。」-これにより、既存のストレージが解放される前にストレージの割り当てが強制的に行われるため、割り当てに失敗した場合、変更せずに例外がスローされます*this。しかし、既存のストレージの再利用を妨げるものは何もありません。その場合、ストレージのコピーオーバーのセマンティクスが指定されていないため、これは指定されません。
サムVarshavchik


2
前述のシーケンスコンテナーではassign[tab:container.seq.req]の要件の前提条件違反のため、それは確かにUB です。
クルミ

回答:


8

あなたのものではないいくつかの例外を除いassignて、文字列に対して非constメンバー関数(つまり)を呼び出すと、その要素への[...]ポインター[...]が無効になります。これは違反を前提assign、その[s, s + n)有効範囲であるので、これは未定義の動作です。

string::operator=(string const&)には、自己割り当てをノーオペレーションにするための言語があります。


1
それでは、無効化のポイントと、前提条件を保持する必要があるポイントとは正確に何ですか?その答えは、メンバー関数が呼び出された後に前提条件が満たされる必要があることを想定しているようです。
クルミ

1
@walnut私は言語弁護士ではありません(特にC ++の知識が豊富な人でもありません)が、シナリオを逆にすると、質問をすることができます。実行中に範囲が無効なる可能性はありassignますか?はいの場合、割り当ての実装の内部に特定のポイントを設定して、無効化が正確に発生する可能性がある場合にマークを付ける必要があります。これは、C ++が行うことではないと思います。私は間違っているかもしれません。
フルイッシュの

2
@Fureeish私も知りませんが、たとえば、LWG issue 526を参照してください。これは、「欠陥ではない」としてクローズされています。これは、ベクター自体に含まれるstd::vector::insert(iterator pos, const T& value)場合に機能する必要があるクロージャーの推奨で言及されvalueています。その参照が呼び出しによって無効になる可能性がある場合でも、機能しないことが許可されます。
クルミ

1
@walnutは、「標準が機能しないことを許可していないため、機能するために必要です。」- 大好きです。本当に... 実際に何が起こるかを尋ねる価値はありますか?そのような状況で議論のコピーを作成するために実装は必要ですか?どうすれば現実的にそれを実装できますか?コンパイラーが不可能なことを行うことを要求する標準について聞いたことがあります-それはそれらのケースの1つですか?とにかく、コメントありがとうございます!
フルイッシュの

1
@Fureeish実際、以前の(現在は削除されている)例は、実際にテストしたいものをテストしていませんでした。これは、libc ++とlibstdc ++の両方が、必要に応じて再割り当てを行う前に実際にコピーを行うことを示す修正された例です。
ウォールナット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.