|
| 1 | +# 未初期化変数の読み取りを誤り起因動作とする [P2795R5] |
| 2 | +* cpp26[meta cpp] |
| 3 | + |
| 4 | +<!-- start lang caution --> |
| 5 | + |
| 6 | +このページはC++26に採用される見込みの言語機能の変更を解説しています。 |
| 7 | + |
| 8 | +のちのC++規格でさらに変更される場合があるため[関連項目](#relative-page)を参照してください。 |
| 9 | + |
| 10 | +<!-- last lang caution --> |
| 11 | + |
| 12 | +## 概要 |
| 13 | +これまで、未初期化変数 (デフォルト初期化された変数) の読み取りは未定義動作として扱われていた。C++26では、この操作が安全上のリスクをもたらさないものとして、「誤り起因動作 (erroneous behavior; 通称 EB)」を新設してそれに割り当てることとした。 |
| 14 | + |
| 15 | +この変更の対象は、デフォルト初期化されたスカラ型 (`void`以外の組み込み型) 変数の読み取りであり、以下のようなケースである。 |
| 16 | + |
| 17 | +```cpp |
| 18 | +// C++23 |
| 19 | +void f(int); |
| 20 | + |
| 21 | +int main() { |
| 22 | + int x; // デフォルト初期化。xは不定値 (indeterminate value) をもつ |
| 23 | + f(x); // 左辺値から右辺値への変換が未定義動作を引き起こす |
| 24 | +} |
| 25 | +``` |
| 26 | +
|
| 27 | +未初期化の値は、コンパイラやターゲット環境によって定義された固定値である。コンパイラにはこの誤りを診断することが許可され、推奨されているが、誤りを無視して有効な読み取りとして扱うことも許可されている。このコードは誤りではあるが、未定義動作に関連する結果に遭遇するリスクはない。 |
| 28 | +
|
| 29 | +誤り起因動作は未定義動作とはちがって、明確に定義された動作をするがコードとして正しくない、ということを規定するものであり、処理系に対して有用な診断を提供するものである。 |
| 30 | +
|
| 31 | +C++26では、不定値で初期化されることを明確に指示する`[[indeterminate]]`属性も導入され、以下のような動作となる: |
| 32 | +
|
| 33 | +```cpp |
| 34 | +// C++26 |
| 35 | +void f(int); |
| 36 | +
|
| 37 | +int main() { |
| 38 | + int x; // xは誤り起因動作を引き起こす未初期化値をもつ |
| 39 | + int y [[indeterminate]]; // 意図して不定値に初期化されることを指示 |
| 40 | +
|
| 41 | + f(x); // 誤り起因動作 (未初期化値の読み取り) |
| 42 | + f(y); // 未定義動作 (不定値の読み取り) |
| 43 | +} |
| 44 | +``` |
| 45 | + |
| 46 | + |
| 47 | +## 仕様 |
| 48 | +- 式が評価された結果として不定値が生成された場合、未定義動作を引き起こす |
| 49 | +- 式が評価された結果として未初期化値が生成された場合、誤り起因動作を引き起こす |
| 50 | + - ただし、`unsigned char`もしくは[`std::byte`](/reference/cstddef/byte.md)型に関しては、未初期化値をもつ変数を同じ型の変数に代入するだけでは誤り起因動作にならず、値の参照や型変換がされることで誤り起因動作を引き起こす |
| 51 | +- 誤り起因動作が引き起こされた結果値は、後続の処理では誤り起因動作を引き起こす値とはみなされない |
| 52 | + |
| 53 | +```cpp |
| 54 | +int g(bool b) { |
| 55 | + unsigned char c; |
| 56 | + unsigned char d = c; // 誤り起因動作ではない。dは誤り起因動作を引き起こす未初期化値をもつ |
| 57 | + |
| 58 | + assert(c == d); // どちらの変数も整数昇格され誤り起因動作となる |
| 59 | + |
| 60 | + int e = d; // 誤り起因動作 (型変換) |
| 61 | + return b ? d : 0; // bがtrueの場合に誤り起因動作 |
| 62 | +} |
| 63 | + |
| 64 | +void h() { |
| 65 | + int d1, d2; |
| 66 | + |
| 67 | + int e1 = d1; // 誤り起因動作 |
| 68 | + int e2 = d1; // 誤り起因動作 |
| 69 | + |
| 70 | + // 処理が続行した場合… |
| 71 | + assert(e1 == e2); // OK。誤り起因動作の結果で生成された値は正常 |
| 72 | + assert(e1 == d1); // 誤り起因動作 |
| 73 | + assert(e2 == d1); // 誤り起因動作 |
| 74 | + |
| 75 | + // 誤り起因動作ではないが |
| 76 | + // d2は誤り起因動作を引き起こす未初期化値をもつ |
| 77 | + std::memcpy(&d2, &d1, sizeof(int)); |
| 78 | + |
| 79 | + assert(e1 == d2); // 誤り起因動作 |
| 80 | + assert(e2 == d2); // 誤り起因動作 |
| 81 | +} |
| 82 | +``` |
| 83 | +
|
| 84 | +### `[[indeterminate]]`属性 |
| 85 | +`[[indeterminate]]`属性は、自動変数が初期状態として意図して不定値をもつことを指示するものであり、自動変数の定義、もしくは関数のパラメータ宣言に適用できる。 |
| 86 | +
|
| 87 | +関数のパラメータが`[[indeterminate]]`属性で宣言される場合、その関数の最初の宣言でそのように宣言されなければならない (注:関数宣言は複数行うことができるが、その最初の宣言で`[[indeterminate]]`属性をつけなければならない)。 |
| 88 | +
|
| 89 | +`[[indeterminate]]`がつけられた変数から読み取りをした場合、未定義動作を引き起こす可能性がある。 |
| 90 | +
|
| 91 | +```cpp |
| 92 | +struct T { |
| 93 | + T() {} |
| 94 | + int x; |
| 95 | +}; |
| 96 | +
|
| 97 | +int h(T t [[indeterminate]]) { |
| 98 | + f(t.x); // この関数呼び出しは未定義動作を引き起こす |
| 99 | + return 0; |
| 100 | +} |
| 101 | +int _ = h(T()); |
| 102 | +``` |
| 103 | + |
| 104 | +### 今後、誤り起因動作に分類される可能性のある操作 |
| 105 | + |
| 106 | +現在、未定義動作に分類される以下の操作は、誤り起因動作に分類できる可能性がある。 |
| 107 | + |
| 108 | +| 操作 | 備考 | |
| 109 | +|------|------| |
| 110 | +| 符号付き整数のオーバーフロー | 演算結果としてオーバーフローした場合に誤った結果になる可能性がある。これは珍しいバグではない。これは安全上の大きな問題ではない | |
| 111 | +| 算術型の変換結果としてその型の表現可能な範囲を超えた | 符号付き整数のオーバーフローと同じ | |
| 112 | +| 誤ったビットシフト (負のシフト幅や、上限を超えたシフト幅) | 符号付き整数のオーバーフローと同じ | |
| 113 | +| ゼロ割り | いくつかの固定値での誤った結果となる可能性がある。影響が不明確であるため、変更にはコストがかかる | |
| 114 | +| 戻り値型が非`void`な関数から返った、もしくは`[[noreturn]]`属性をつけた関数から返った | [`std::terminate()`](/reference/exception/terminate.md)が呼ばれる可能性がある。変更には軽いコストがかかるが、その変更にどの程度の価値があるかは不明 | |
| 115 | +| 抽象クラスのコンストラクタ・デストラクタからの純粋仮想関数の呼び出し | 特定の純粋仮想ハンドラが呼ばれる可能性がある。実装によってはすでに誤り起因動作のように扱われている可能性がある | |
| 116 | +| 契約違反 | 契約に関する現在の策定作業では、契約違反時になにが起こるべきかという問題に直面している。誤り起因動作という概念は有用な回答を与えてくれる可能性がある | |
| 117 | + |
| 118 | + |
| 119 | + |
| 120 | +## 参照 |
| 121 | +- [P2795R5 Erroneous behaviour for uninitialized reads](https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2795r5.html) |
0 commit comments