TriviallyCopyableでないオブジェクトに対してstd::memcpyの動作が不定になるのはなぜですか?
質問
から http://en.cppreference.com/w/cpp/string/byte/memcpy :
もし、オブジェクトが TriviallyCopyable (例:スカラー、配列、C互換構造体)でない場合、動作は未定義です。
私の仕事場では
std::memcpy
を使用して TriviallyCopyable でないオブジェクトをビット単位でスワップするために長い間使用してきました。
void swapMemory(Entity* ePtr1, Entity* ePtr2)
{
static const int size = sizeof(Entity);
char swapBuffer[size];
memcpy(swapBuffer, ePtr1, size);
memcpy(ePtr1, ePtr2, size);
memcpy(ePtr2, swapBuffer, size);
}
で、特に問題はありませんでした。
を悪用するのは些細なことだと理解しています。
std::memcpy
を悪用し、下流で未定義の挙動を引き起こすことは理解しました。しかしながら、私の質問です。
の動作はなぜ
std::memcpy
の動作は、TriviallyCopyable でないオブジェクトで使用された場合、未定義になるのでしょうか?なぜ規格はそれを指定する必要があると考えたのでしょうか?
アップデイト
の内容は http://en.cppreference.com/w/cpp/string/byte/memcpy は、この投稿と投稿に対する回答を受けて修正しました。現在の記述では
もし、オブジェクトが TriviallyCopyable (例えばスカラー、配列、C 互換の構造体) でない場合、プログラムがターゲットオブジェクトのデストラクタの効果に依存しない限り、動作は未定義です (このオブジェクトは
memcpy
によって実行されない) とターゲットオブジェクトのライフタイム (終了するが、開始しない) の効果にプログラムが依存しない限り、動作は未定義です。memcpy
によって開始されていない) の寿命は、placement-new のような他の手段によって開始されます。
PS
Cubbi さんのコメントです。
下流でUBを保証してしまうと、プログラム全体が不定になってしまうので、@RSahuさん。しかし、私はこのケースでUBを回避することが可能であるように見えることに同意し、それに応じてcppreferenceを変更しました。
どのように解決するのですか?
<ブロッククオート
の動作は、なぜ
std::memcpy
の動作は、TriviallyCopyableでないオブジェクトで使用された場合、不定になるのでしょうか?
それはありません! しかし、一旦、非自明なコピー可能型のあるオブジェクトの基礎となるバイトをその型の別のオブジェクトにコピーすると ターゲットオブジェクトは生きていない . 私たちはそのストレージを再利用することでそれを破壊し、コンストラクターの呼び出しによってそれを活性化させていません。
ターゲットオブジェクトを使用すること、つまりそのメンバー関数を呼び出したり、そのデータメンバーにアクセスすることは、明らかに定義されていない [基本.生活]/6 であり、その後の暗黙のデストラクタ呼び出しも同様です。 [基本的な.生活]/4 であり、自動的な保存期間を持つターゲットオブジェクトのための どのように 未定義の動作が遡及的に . [イントロ.実行]/5:
しかし,そのような実行が未定義の操作を含んでいる場合,この国際規格は,実装に対して何の要求もしない。 国際規格は、その入力でそのプログラムを実行する実装に何の要件も課さない そのプログラムをその入力で実行すること ( に関しても。 最初の未定義の操作の前の操作に関しても ).
もし実装が、あるオブジェクトが死んでいて、必然的に未定義のさらなる操作の対象となることを見抜くと、...それはあなたのプログラムのセマンティクスを変更することで反応するかもしれません。このような場合
memcpy
の呼び出しからずっと。そしてこの考察は、オプティマイザーと彼らが作る特定の仮定について考えると、非常に現実的なものになります。
標準ライブラリは些細なコピー可能な型のために特定の標準ライブラリアルゴリズムを最適化することができ、また許されていることに注意する必要がありますが。
std::copy
は、通常、コピー可能な型へのポインタに対して
memcpy
を呼び出します。ですから
swap
.
ですから、通常の一般的なアルゴリズムを使うことに専念し、適切な低レベルの最適化はコンパイラに任せましょう。これは、そもそも些細にコピー可能な型のアイデアが考案された理由の一部です。ある種の最適化の合法性を判断するためです。また、これにより、言語の矛盾や仕様が定まっていない部分について心配する必要がなくなり、脳を傷つけずに済みます。
関連
-
[解決済み】構造体のベクター初期化について
-
[解決済み】C++でint型に無限大を設定する
-
[解決済み】識別子 "string "は未定義?
-
[解決済み] エラーが発生する。ISO C++は型を持たない宣言を禁じています。
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み] ベースクラスのコンストラクタを呼び出す際のルールは?
-
[解決済み] const std::string & をパラメータとして渡す時代は終わったのでしょうか?
-
[解決済み】f(i = -1, i = -1)の挙動が未定義なのはなぜ?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン