Skip to content

Commit d8f8beb

Browse files
committed
observable_checkpoint : ルールのカバー状況と動機となる例を記載 #1418
1 parent 610b4e4 commit d8f8beb

1 file changed

Lines changed: 11 additions & 13 deletions

File tree

reference/utility/observable_checkpoint.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@ namespace std {
1515
1616
C++では未定義動作を含むプログラムに対して、コンパイラは未定義動作が「将来」発生することを根拠に、それより「前」の操作を削除・変更する最適化を行うことが許容される。これは「タイムトラベル最適化」と呼ばれる。
1717
18-
例として以下のコードでは、(2)で未定義動作があるため、(1)の出力すら省略される可能性がある:
18+
例として以下のコードでは、nullポインタが渡された場合にエラーメッセージを出力してからデリファレンスに到達する意図だが、コンパイラは`++*p`が未定義動作にならないと仮定して`p`がnullでないと推論し、`if(!p)`の分岐ごと削除できる。その結果、エラーメッセージが出力されることなくクラッシュする可能性がある:
1919
2020
```cpp
2121
#include <cstdio>
2222
23-
int main() {
24-
std::printf("Hello, "); // (1) 出力
25-
int* p = nullptr;
26-
*p = 42; // (2) 未定義動作(nullポインタのデリファレンス)
27-
std::printf("World!\n"); // (3) 出力
23+
void inc(int* p) {
24+
if (!p) std::fputs("Null!\n", stderr); // (1) エラー出力(削除される可能性がある)
25+
++*p; // (2) pがnullなら未定義動作
2826
}
2927
```
3028

@@ -34,23 +32,23 @@ int main() {
3432
#include <cstdio>
3533
#include <utility>
3634

37-
int main() {
38-
std::printf("Hello, "); // (1) 出力
39-
std::observable_checkpoint(); // ここまでの観測可能な動作を保護
40-
int* p = nullptr;
41-
*p = 42; // (2) 未定義動作
42-
std::printf("World!\n"); // (3) 出力
35+
void inc(int* p) {
36+
if (!p) std::fputs("Null!\n", stderr); // (1) エラー出力
37+
std::observable_checkpoint(); // ここまでの観測可能な動作を保護
38+
++*p; // (2) pがnullなら未定義動作
4339
}
4440
```
4541
46-
この場合、`std::observable_checkpoint()`によって(1)の出力`"Hello, "`が保護され、(2)の未定義動作があっても遡って消去されない。
42+
この場合、`std::observable_checkpoint()`によって(1)のエラー出力が保護され、(2)の未定義動作があっても遡って消去されない。
4743
4844
`std::observable_checkpoint()`の明示的な呼び出しに加え、以下の操作も暗黙的に観測可能チェックポイントを設置する:
4945
5046
- C標準の入出力関数([`std::printf()`](/reference/cstdio/printf.md)、[`std::fwrite()`](/reference/cstdio/fwrite.md)など)のうち、ファイルへのデータ書き込みを行う関数呼び出しからの復帰
5147
- デフォルトの[`sync_with_stdio(true)`](/reference/ios/ios_base/sync_with_stdio.md)の状態では、[`std::cout`](/reference/iostream/cout.md)などの標準ストリームは`stdout`と同期しているため、これに該当する
5248
- [`std::ofstream`](/reference/fstream/basic_ofstream.md)などのファイルストリーム出力時の[`std::basic_filebuf`](/reference/fstream/basic_filebuf.md)のオーバーフロー操作(バッファの内容をファイルに書き出した時点)
5349
- Unicode出力時の[`std::print()`](/reference/print/print.md) / [`std::println()`](/reference/print/println.md)の内部出力関数[`std::vprint_unicode()`](/reference/print/vprint_unicode.md)によるターミナルへの書き込み([`<ostream>`](/reference/ostream.md)版および[`<print>`](/reference/print.md)版)
50+
- Unicode出力時はネイティブのOS API(Windowsでの`WriteConsoleW()`など)を直接使用しC標準の入出力を経由しないため、このルールで明示的にカバーしている
51+
- 非Unicode出力時はC標準I/O関数を経由するため、上記のC標準入出力関数のルールでカバーされる
5452
5553
上記の例では`std::printf()`がC標準の入出力関数であるため、(1)の呼び出しの復帰が暗黙の観測可能チェックポイントとなり、`"Hello, "`の出力は(2)の未定義動作によって遡って消去されない。
5654

0 commit comments

Comments
 (0)