|
11 | 11 |
|
12 | 12 | ## 概要
|
13 | 13 |
|
14 |
| -`new`式ではなく`malloc()`等他の方法で確保されたメモリ領域には、明示的にオブジェクトの構築を行うまで想定する型のオブジェクトは生存期間内になく、そのアクセス(読み書き)は未定義動作となる。 |
| 14 | +`new`式ではなく`malloc()`等他の方法で確保されたメモリ領域には、明示的にオブジェクトの構築を行うまで想定する型のオブジェクトは生存期間内になく、そのアクセス(読み書き)は未定義動作を引き起こす。 |
15 | 15 |
|
16 | 16 | ```cpp
|
17 | 17 | struct X {
|
@@ -47,11 +47,11 @@ X *make_x() {
|
47 | 47 | }
|
48 | 48 | ```
|
49 | 49 |
|
50 |
| -一番最初の`malloc()`を用いるコードは主にCのコードと共有する部分で現れることがあり、Cでは問題ないもののC++コードとしてコンパイルした場合にのみ未定義動作となる。C++20からは、この様なコードのC/C++相互運用性向上のために、明示的にオブジェクトを構築する必要がないとみなすことのできる一部の型(*implicit-lifetime types*)に限って、未初期化領域へのアクセス時に暗黙的に(自動的に)オブジェクトが構築される様になり、未定義動作を回避できる様になる。 |
| 50 | +一番最初の`malloc()`を用いるコードは主にCのコードと共有する部分で現れることがあり、Cでは問題ないもののC++コードとしてコンパイルした場合にのみ未定義動作を引き起こす。C++20からは、この様なコードのC/C++相互運用性向上のために、明示的にオブジェクトを構築する必要がないとみなすことのできる一部の型(*implicit-lifetime types*)に限って、未初期化領域へのアクセス時に暗黙的に(自動的に)オブジェクトが構築される様になり、未定義動作を回避できる様になる。 |
51 | 51 |
|
52 | 52 | ## 問題となる他の例
|
53 | 53 |
|
54 |
| -以下の例はこの変更によって解決される例であり、C++17以前は未定義動作となるものである。 |
| 54 | +以下の例はこの変更によって解決される例であり、C++17以前は未定義動作を引き起こすものである。 |
55 | 55 |
|
56 | 56 | ### `operator new`
|
57 | 57 |
|
@@ -109,7 +109,7 @@ void process(Stream *stream) {
|
109 | 109 | }
|
110 | 110 | ```
|
111 | 111 |
|
112 |
| -1つの領域に複数の型のオブジェクトが同時に生存期間内にあることはないため、`stream->read()`が内部で`Foo, Bar`どちらかのオブジェクトを構築していたとしても、`#1`と`#2`のどちらかのパスは未定義動作となる。そして、この様なコードでは多くの場合、明示的にオブジェクトが構築されることはない。 |
| 112 | +1つの領域に複数の型のオブジェクトが同時に生存期間内にあることはないため、`stream->read()`が内部で`Foo, Bar`どちらかのオブジェクトを構築していたとしても、`#1`と`#2`のどちらかのパスは未定義動作を引き起こす。そして、この様なコードでは多くの場合、明示的にオブジェクトが構築されることはない。 |
113 | 113 |
|
114 | 114 | ### 動的配列の実装
|
115 | 115 |
|
@@ -154,7 +154,7 @@ int main() {
|
154 | 154 | }
|
155 | 155 | ```
|
156 | 156 |
|
157 |
| -C++においては、ポインタに対する演算(`+ -`など)はそのポインタが配列オブジェクトを指している場合にのみ有効となるが、例中の`#a #b #c #d #e #f`で使用されるポインタの指す領域にはいずれも配列オブジェクトが生存期間にない(正確には、非配列オブジェクトのポインタの場合は要素数1の配列として扱われるが、この例の場合はそれを満たすことはほぼなく、満たさないものとする)。そのため、そのポインタをイテレータの様に使用することは未定義動作となる。 |
| 157 | +C++においては、ポインタに対する演算(`+ -`など)はそのポインタが配列オブジェクトを指している場合にのみ有効となるが、例中の`#a #b #c #d #e #f`で使用されるポインタの指す領域にはいずれも配列オブジェクトが生存期間にない(正確には、非配列オブジェクトのポインタの場合は要素数1の配列として扱われるが、この例の場合はそれを満たすことはほぼなく、満たさないものとする)。そのため、そのポインタをイテレータの様に使用することは未定義動作を引き起こす。 |
158 | 158 |
|
159 | 159 | ## 仕様
|
160 | 160 |
|
@@ -224,13 +224,13 @@ C++においては、ポインタに対する演算(`+ -`など)はそのポ
|
224 | 224 |
|
225 | 225 | ### 暗黙的なオブジェクト構築
|
226 | 226 |
|
227 |
| -オブジェクトを暗黙的に構築する操作では、そうすることでプログラムが定義された振る舞いをするようになる(すなわち、未定義動作が回避できる)場合に、*implicit-lifetime types*の0個以上のオブジェクトを暗黙的に構築しその生存期間を開始させる。そのような、暗黙的なオブジェクト構築によってプログラムに定義された振る舞いをもたらすオブジェクトの集合が1つも存在しない場合は未定義動作となる(これは今まで通り)。逆に、そのようなオブジェクトの集合が複数存在している場合は、どのオブジェクトが暗黙的に構築されるかは未規定(これは、都度適切なオブジェクトが選択され構築されることを意図している)。 |
| 227 | +オブジェクトを暗黙的に構築する操作では、そうすることでプログラムが定義された振る舞いをするようになる(すなわち、未定義動作が回避できる)場合に、*implicit-lifetime types*の0個以上のオブジェクトを暗黙的に構築しその生存期間を開始させる。そのような、暗黙的なオブジェクト構築によってプログラムに定義された振る舞いをもたらすオブジェクトの集合が1つも存在しない場合は未定義動作を引き起こす(これは今まで通り)。逆に、そのようなオブジェクトの集合が複数存在している場合は、どのオブジェクトが暗黙的に構築されるかは未規定(これは、都度適切なオブジェクトが選択され構築されることを意図している)。 |
228 | 228 |
|
229 | 229 | 暗黙的なオブジェクト構築が行われる場合、そのサブオブジェクトの*implicit-lifetime types*のオブジェクトも暗黙的に構築され生存期間が開始されるが、*implicit-lifetime types*ではないオブジェクトは暗黙的に構築されないため明示的な初期化が必要となる。
|
230 | 230 |
|
231 | 231 | 暗黙的なオブジェクト構築を行わなくてもプログラムが定義された振る舞いをする(未定義動作とならない)場合、対象の操作は暗黙的にオブジェクトを構築せず、通常の効果のみをもたらす。
|
232 | 232 |
|
233 |
| -さらに、オブジェクトを暗黙的に構築する操作の内メモリを確保する関数等一部の操作では、暗黙的なオブジェクト構築によって作成されたオブジェクトを指す適切なポインタを返すことが規定される。これらの操作においては、そのポインタ値を返すことでプログラムが定義された振る舞いをするようになる場合に、指定された領域の先頭アドレスをアドレスとする暗黙的に構築されたオブジェクトの1つを選択し、そのオブジェクトを指すポインタ値を生成しそれを返す。そのような、プログラムに定義された振る舞いをもたらすようなポインタ値が存在しない場合は未定義動作となる。逆に、そのようなポインタ値が複数存在している場合は、どのポインタ値が生成されるかは未規定。 |
| 233 | +さらに、オブジェクトを暗黙的に構築する操作の内メモリを確保する関数等一部の操作では、暗黙的なオブジェクト構築によって作成されたオブジェクトを指す適切なポインタを返すことが規定される。これらの操作においては、そのポインタ値を返すことでプログラムが定義された振る舞いをするようになる場合に、指定された領域の先頭アドレスをアドレスとする暗黙的に構築されたオブジェクトの1つを選択し、そのオブジェクトを指すポインタ値を生成しそれを返す。そのような、プログラムに定義された振る舞いをもたらすようなポインタ値が存在しない場合は未定義動作を引き起こす。逆に、そのようなポインタ値が複数存在している場合は、どのポインタ値が生成されるかは未規定。 |
234 | 234 |
|
235 | 235 | そのようなポインタ生成を行わなくてもプログラムが定義された振る舞いをする場合、そのようなポインタ値は生成されず、対象の操作は通常のポインタ値を返す。
|
236 | 236 |
|
@@ -381,7 +381,7 @@ unique_ptr<char[]> Stream::read() {
|
381 | 381 |
|
382 | 382 | ## この機能が必要になった背景・経緯
|
383 | 383 |
|
384 |
| -例に上がっているようなコードはC言語では一般的な操作であり、Cでは問題がない。このようなコードはCとC++のコード共有部分でC++コードとして現れる可能性があり、その場合には未定義動作となる。 |
| 384 | +例に上がっているようなコードはC言語では一般的な操作であり、Cでは問題がない。このようなコードはCとC++のコード共有部分でC++コードとして現れる可能性があり、その場合には未定義動作を引き起こす。 |
385 | 385 |
|
386 | 386 | また、動的配列の例などは、`std::vector`の実装において問題となることで、そこは完全にC++のコードでありながらC++がサポートしてないことを行うことになってしまう。
|
387 | 387 |
|
@@ -413,4 +413,4 @@ unique_ptr<char[]> Stream::read() {
|
413 | 413 | - [P0593R0 What to do with buffers that are not arrays, and undefined behavior thereof?](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0593r0.html)
|
414 | 414 | - [CWG Issue 2325. `std::launder` and reuse of character buffers](https://cplusplus.github.io/CWG/issues/2325.html)
|
415 | 415 | - [CWG Issue 2605. Implicit-lifetime aggregates](https://cplusplus.github.io/CWG/issues/2605.html)
|
416 |
| -- [P1839R5 Accessing Object Representations](https://wg21.link/p1839r5) |
| 416 | +- [P1839R5 Accessing Object Representations](https://wg21.link/p1839r5) |
0 commit comments