質問するログイン新規登録

回答編集履歴

2

仕様の関連項目を追記

2020/12/17 08:45

投稿

SaitoAtsushi
SaitoAtsushi

スコア5740

answer CHANGED
@@ -4,4 +4,35 @@
4
4
 
5
5
  ----
6
6
 
7
- メモリのレイアウトに関すること以外で言えば `*p_s1` は std::string として初期化されていません。 代入は必ずしも素朴なビットパターンのコピーでありませんのできちんと初期化されて std::string 型として整合性が取れている状態になっていないと何が起こるかわかりません。
7
+ メモリのレイアウトに関すること以外で言えば `*p_s1` は std::string として初期化されていません。 代入は必ずしも素朴なビットパターンのコピーでありませんのできちんと初期化されて std::string 型として整合性が取れている状態になっていないと何が起こるかわかりません。
8
+
9
+ # 仕様の関連項目
10
+
11
+ 様々なルールの組み合わせなので今回の事情にちょうどよい内容がまとまっているものというとちょっと示せないのですが、とりあえずおおざっぱにだけ仕様 (C++11) の関連個所をリンク付きで抜き出しておきます。 規格を読み解くのは大変なのでわからないところはこのあたりに出てくる用語から適当にググってください。
12
+
13
+ - まず、 [trivially copyable な型はバイト列としてコピー可能である](https://timsong-cpp.github.io/cppwp/n3337/basic#types-2) (std::memcpy できる) と示されています。
14
+ - [スカラ型や配列型や trivially copyable class などは trivially copyable](https://timsong-cpp.github.io/cppwp/n3337/basic.types#9) です。
15
+ - [trivially copyable class であるための要件](https://timsong-cpp.github.io/cppwp/n3337/class#6)はユーザー定義のコンストラクタを持っていないことなどのオブジェクトの構築に関する制約と、データメンバや基底も trivially copyable でなければならないという制約を満たすことです。 ただし、具体的な要件の内容は C++14 以降、 C++17 以降で若干の変更があるようです。 規格の詳細を確認するのは面倒なのでよくわからなければ [`std::is_trivially_copyable`](https://timsong-cpp.github.io/cppwp/n3337/meta.unary.prop#tab:type-traits.properties) を使ってチェックしてみればよいです。
16
+
17
+ そして `std::string` は trivially copyable class ではないのでこれをバイト列としてコピーして戻したときにオブジェクトとして正しいことは保証されないわけです。。
18
+
19
+ ```cpp
20
+ #include <iostream>
21
+ #include <type_traits>
22
+ #include <string>
23
+
24
+ int main(void) {
25
+ // 0 が出力される。 std::string は trivially copyable ではない。
26
+ std::cout << std::is_trivially_copyable<std::string>() << std::endl;
27
+ return 0;
28
+ }
29
+ ```
30
+
31
+ レイアウトに関する保証としては、
32
+
33
+ - [同じアクセス制御 (`private`, `public`, `protect` の指定が同じということ) を持つデータメンバは宣言の順序で配置され](https://timsong-cpp.github.io/cppwp/n3337/class.mem#14)ますが、逆に言えば違うアクセス制御のデータメンバはどちらが先でどちらが後かというのは並べ替えられる可能性もあるということです。
34
+ - 更に、スタンダードレイアウトと呼ばれる要件を満たす場合には [`offsetof` マクロを使える](https://timsong-cpp.github.io/cppwp/n3337/support.types#4)こと、[同じ内容の同じ内容の構造体型のレイアウトが互換性を持つこと](https://timsong-cpp.github.io/cppwp/n3337/class.mem#17)などの保証が与えられます。
35
+ - [オブジェクトがスタンダードレイアウトになる要件](https://timsong-cpp.github.io/cppwp/n3337/class#7)としては仮想関数を持たないことや全てのデータメンバが同じアクセス制御を持つことなどが求められています。
36
+ - オブジェクト内には[詰め物 (padding) がある可能性](https://timsong-cpp.github.io/cppwp/n3337/expr#sizeof-2)もあり、データメンバとデータメンバの間に無意味なバイト列が挟まっていることもあります。 (アラインの調整などの理由で。)
37
+
38
+ 言語の仕様として保証していないことであってもすぐさま駄目なわけではなく、処理系・実行環境としてなんらかの保証を与えている場合もあります。 ただ、検証するのが面倒ですし、移植性も損なうので特別に強い理由があるのでなければ言語仕様が保証する範囲内でやるのが好ましくはあります。

1

要件をより厳密に

2020/12/17 08:45

投稿

SaitoAtsushi
SaitoAtsushi

スコア5740

answer CHANGED
@@ -1,4 +1,4 @@
1
- 言語仕様としてはオブジェクトがどのようなメモリレイアウトになるのかということを限定的な保証しかしていません。 POD 要件を満たす場合を除いてはバイト単位でのコピーがオブジェクトのコピーとしても正しいとは保証されません。 未定義です。
1
+ 言語仕様としてはオブジェクトがどのようなメモリレイアウトになるのかということを限定的な保証しかしていません。 trivially copyable 要件を満たす場合を除いてはバイト単位でのコピーがオブジェクトのコピーとしても正しいとは保証されません。 未定義です。
2
2
 
3
3
  「どうしてそうなってしまうのか」ということに (言語仕様に基づいた) 説明をするなら「保証されていないことをしているから」というのが解です。
4
4