Skip to content

Commit 901f332

Browse files
committed
C++26対応としてobservable_checkpointを追加 #1418
1 parent 73bed16 commit 901f332

4 files changed

Lines changed: 157 additions & 3 deletions

File tree

lang/cpp26.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ C++26とは、2026年中に改訂される予定の、C++バージョンの通
510510
- [`<format>`](/reference/format.md)ヘッダの以下の機能
511511
- [`std::format_error`](/reference/format/format_error.md)
512512
- [`<memory>`](/reference/memory.md)に、ポインタのアライメントを判定する[`std::is_sufficiently_aligned()`](/reference/memory/is_sufficiently_aligned.md)関数を追加。
513-
- [`<utility>`](/reference/utility.md)に、タイムトラベル最適化を抑止するための観測可能ポイントとして[`std::observable_checkpoint()`](/reference/utility/observable_checkpoint.md.nolink)を追加
513+
- [`<utility>`](/reference/utility.md)に、タイムトラベル最適化を抑止するための観測可能ポイントとして[`std::observable_checkpoint()`](/reference/utility/observable_checkpoint.md)を追加
514514
- [`std::exception_ptr`](/reference/exception/exception_ptr.md)を指定した例外型にキャストする[`std::exception_ptr_cast()`](/reference/exception/exception_ptr_cast.md)関数を追加
515515
- [`<compare>`](/reference/compare.md)に、型の順序を取得する[`std::type_order`](/reference/compare/type_order.md.nolink)クラスを追加
516516

lang/cpp26/feature_test_macros.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
|`__cpp_lib_linalg`|`202412L`|線形代数ライブラリとして[`<linalg>`](/reference/linalg.md)を追加|[`<linalg>`](/reference/linalg.md)|
118118
|`__cpp_lib_mdspan`|`202406L`|[`std::mdspan`](/reference/mdspan/mdspan.md)に対する[`std::dextents`](/reference/mdspan/extents.md)指定の冗長さを解決する[`std::dims`](/reference/mdspan/extents.md)を追加|[`<mdspan>`](/reference/mdspan.md)|
119119
|`__cpp_lib_not_fn`|`202306L`|[`std::not_fn()`](/reference/functional/not_fn.md)に、非型テンプレート引数として関数を指定するオーバーロードを追加|[`<functional>`](/reference/functional.md)|
120-
|`__cpp_lib_observable_checkpoint`|`202506L`|[`<utility>`](/reference/utility.md)[`std::observable_checkpoint()`](/reference/utility/observable_checkpoint.md.nolink)を追加|[`<utility>`](/reference/utility.md)|
120+
|`__cpp_lib_observable_checkpoint`|`202506L`|[`<utility>`](/reference/utility.md)[`std::observable_checkpoint()`](/reference/utility/observable_checkpoint.md)を追加|[`<utility>`](/reference/utility.md)|
121121
|`__cpp_lib_optional`|`202506L`|[`std::optional`](/reference/optional/optional.md)に、参照を保持するための`T&`の部分特殊化を追加|[`<optional>`](/reference/optional.md)|
122122
|`__cpp_lib_optional_range_support`|`202406L`|[`std::optional`](/reference/optional/optional.md)にイテレータインタフェースを追加|[`<optional>`](/reference/optional.md)|
123123
|`__cpp_lib_out_ptr`|`202311L`||[`<memory>`](/reference/memory.md)|

reference/utility.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,12 @@
6868
| [`to_underlying`](utility/to_underlying.md) | 列挙値を基底型に変換する (function template) | C++23 |
6969

7070

71-
## 到達しない表明
71+
## 未定義動作
7272

7373
| 名前 | 説明 | 対応バージョン |
7474
|------|------|----------------|
7575
| [`unreachable`](utility/unreachable.md) | コードパス不到達を表明する (function) | C++23 |
76+
| [`observable_checkpoint`](utility/observable_checkpoint.md) | 観測可能チェックポイントを設置する (function) | C++26 |
7677

7778

7879
##
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# observable_checkpoint
2+
* utility[meta header]
3+
* function[meta id-type]
4+
* std[meta namespace]
5+
* cpp26[meta cpp]
6+
7+
```cpp
8+
namespace std {
9+
void observable_checkpoint() noexcept;
10+
}
11+
```
12+
13+
## 概要
14+
観測可能チェックポイント (observable checkpoint) を設置する。
15+
16+
C++では未定義動作を含むプログラムに対して、コンパイラは未定義動作が「将来」発生することを根拠に、それより「前」の操作を削除・変更する最適化を行うことが許容される。これは「タイムトラベル最適化」と呼ばれる。
17+
18+
例として以下のコードでは、(2)で未定義動作があるため、(1)の出力すら省略される可能性がある:
19+
20+
```cpp
21+
#include <cstdio>
22+
23+
int main() {
24+
std::printf("Hello, "); // (1) 出力
25+
int* p = nullptr;
26+
*p = 42; // (2) 未定義動作(nullポインタのデリファレンス)
27+
std::printf("World!\n"); // (3) 出力
28+
}
29+
```
30+
31+
`std::observable_checkpoint()`を呼び出すと、その時点で観測可能チェックポイントが設置される。観測可能チェックポイントより前に完了した操作の観測可能な動作は、その後に発生する未定義動作によって遡って無効化されないことが保証される。これにより、タイムトラベル最適化を抑止できる:
32+
33+
```cpp
34+
#include <cstdio>
35+
#include <utility>
36+
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) 出力
43+
}
44+
```
45+
46+
この場合、`std::observable_checkpoint()`によって(1)の出力`"Hello, "`が保護され、(2)の未定義動作があっても遡って消去されない。
47+
48+
`std::observable_checkpoint()`の明示的な呼び出しに加え、以下の操作も暗黙的に観測可能チェックポイントを設置する:
49+
50+
- C標準の入出力関数([`std::printf()`](/reference/cstdio/printf.md)[`std::fwrite()`](/reference/cstdio/fwrite.md)など)のうち、ファイルへのデータ書き込みを行う関数呼び出しからの復帰
51+
- [`std::basic_filebuf`](/reference/fstream/basic_filebuf.md)のオーバーフロー操作(出力シーケンスへの書き込み完了時)
52+
- 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)版)
53+
54+
上記の例では`std::printf()`がC標準の入出力関数であるため、(1)の呼び出しの復帰が暗黙の観測可能チェックポイントとなり、`"Hello, "`の出力は(2)の未定義動作によって遡って消去されない。
55+
56+
57+
## 効果
58+
観測可能チェックポイントを設置する。
59+
60+
61+
## 例外
62+
投げない
63+
64+
65+
## 備考
66+
- `std::observable_checkpoint()`の呼び出し自体は観測可能な動作ではない。コンパイラはこの関数を組み込み関数 (intrinsic) として実装でき、最適化後にゼロ命令のコードを生成できる
67+
- `volatile`アクセスは暗黙の観測可能チェックポイントではない。`volatile`アクセスの順序保証は従来の happens before 規則に従う
68+
- この関数はフリースタンディング環境でも使用可能である
69+
70+
71+
##
72+
### 基本的な使い方
73+
```cpp example
74+
#include <cstdio>
75+
#include <utility>
76+
77+
int table[10];
78+
79+
void process(int i) {
80+
std::printf("processing %d\n", i); // (1) 暗黙の観測可能チェックポイント
81+
table[i] = i * i; // i が範囲外なら未定義動作
82+
}
83+
84+
int main() {
85+
for (int i = 0; i < 12; ++i) {
86+
process(i);
87+
}
88+
}
89+
```
90+
91+
この例では、`i`が10以上になると`table[i]`で範囲外アクセスの未定義動作が発生する。しかし`std::printf()`はC標準の入出力関数であり、呼び出しの復帰が暗黙の観測可能チェックポイントとなる。そのため、`i`が0から9までの反復で出力された`"processing 0"`から`"processing 9"`は、その後の未定義動作によって遡って消去されることはない。
92+
93+
#### 出力例
94+
```
95+
processing 0
96+
processing 1
97+
processing 2
98+
processing 3
99+
processing 4
100+
processing 5
101+
processing 6
102+
processing 7
103+
processing 8
104+
processing 9
105+
processing 10
106+
processing 11
107+
```
108+
109+
### 明示的なチェックポイントの設置
110+
```cpp example
111+
#include <cstdio>
112+
#include <utility>
113+
114+
int main() {
115+
int result = 0;
116+
117+
for (int i = 1; i <= 5; ++i) {
118+
result += i;
119+
}
120+
std::printf("sum = %d\n", result);
121+
std::observable_checkpoint(); // ここまでの出力を保護
122+
123+
// 以降のコードに未定義動作があっても
124+
// "sum = 15" の出力は保証される
125+
int* p = nullptr;
126+
*p = 0; // 未定義動作
127+
}
128+
```
129+
* std::observable_checkpoint[color ff0000]
130+
131+
#### 出力例
132+
```
133+
sum = 15
134+
```
135+
136+
137+
## バージョン
138+
### 言語
139+
- C++26
140+
141+
### 処理系
142+
- [Clang](/implementation.md#clang): 22 [mark noimpl]
143+
- [GCC](/implementation.md#gcc): 15 [mark noimpl]
144+
- [Visual C++](/implementation.md#visual_cpp): 2026 Update 2 [mark noimpl]
145+
146+
147+
## 関連項目
148+
- [`unreachable`](unreachable.md)
149+
150+
151+
## 参照
152+
- [P1494R5 Partial program correctness](https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/p1494r5.html)
153+
- [P3641R0 Rename `std::observable` to `std::observable_checkpoint`, and add a feature-test macro](https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3641r0.html)

0 commit comments

Comments
 (0)