文書の一覧
全部で44本あります。
もくじ
- N4956 Concurrency TS2 PDTS
- N4958 Working Draft, Programming Languages -- C++
- N4959 Editors' Report, Programming Languages -- C++
- N4960 Business Plan and Convener's Report: ISO/IEC JTC1/SC22/WG21 (C++)
- P0124R7 Linux-Kernel Memory Model
- P0124R8 Linux-Kernel Memory Model
- P0963R1 Structured binding declaration as a condition
- P1068R8 Vector API for random number generation
- P1967R11 #embed - a simple, scannable preprocessor-based resource acquisition method
- P2407R5 Freestanding Library: Partial Classes
- P2521R5 Contract support -- Record of SG21 consensus
- P2728R6 Unicode in the Library, Part 1: UTF Transcoding
- P2746R3 Deprecate and Replace Fenv Rounding Modes
- P2795R3 Erroneous behaviour for uninitialized reads
- P2821R4 span.at()
- P2833R1 Freestanding Library: inout expected span
- P2845R2 Formatting of std::filesystem::path
- P2863R1 Review Annex D for C++26
- P2864R1 Remove Deprecated Arithmetic Conversion on Enumerations From C++26
- P2865R2 Remove Deprecated Array Comparisons from C++26
- P2868R1 Remove Deprecated std::allocator Typedef From C++26
- P2869R1 Remove Deprecated shared_ptr Atomic Access APIs From C++26
- P2870R1 Remove basic_string::reserve() From C++26
- P2871R1 Remove Deprecated Unicode Conversion Facets From C++26
- P2875R1 Undeprecate polymorphic_allocator::destroy For C++26
- P2878R5 Reference checking
- P2885R1 Requirements for a Contracts syntax
- P2890R0 Contracts on lambdas
- P2894R0 Constant evaluation of Contracts
- P2896R0 Outstanding design questions for the Contracts MVP
- P2905R2 Runtime format strings
- P2909R0 Dude, where's my char?
- P2933R0 std::simd overloads for <bit> header
- P2935R0 An Attribute-Like Syntax for Contracts
- P2944R1 Comparisons for reference_wrapper
- P2951R2 Shadowing is good for safety
- P2952R0 auto& operator=(X&&) = default
- P2953R0 Forbid defaulting operator=(X&&) &&
- P2954R0 Contracts and virtual functions for the Contracts MVP
- P2955R0 Safer Range Access
- P2956R0 Add saturating library support to std::simd
- P2957R0 Contracts and coroutines
- P2958R0 typeof and typeof_unqual
- P2960R0 Concurrency TS Editor's report for N4956
- おわり
N4956 Concurrency TS2 PDTS
Concurrency TS v2の最新のワーキングドラフト
N4958 Working Draft, Programming Languages -- C++
N4959 Editors' Report, Programming Languages -- C++
↑の変更点をまとめた文書。
N4960 Business Plan and Convener's Report: ISO/IEC JTC1/SC22/WG21 (C++)
ビジネスユーザ向けのC++およびWG21の現状報告書。
P0124R7 Linux-Kernel Memory Model
↓
P0124R8 Linux-Kernel Memory Model
Linuxカーネルにおけるメモリモデルについて説明する文書。
この文書は、C/C++標準化委員会がメモリモデルに関する既存の慣行・実装としてLinuxカーネルにおけるものを参照する際に役立てることを意図したものです。
P0963R1 Structured binding declaration as a condition
構造化束縛宣言を条件式を書くところで書けるようにする提案。
構造化束縛宣言は変数宣言の変種であり、ほぼ変数宣言をかけるところなら同様に書くことができます。しかし、if
文等の条件式はその例外であり、条件式に直接構造化束縛宣言を書くことはできません。
例えば次のような何かパースを行う関数があったとき
struct parse_window { char const *first; char const *last; }; auto parse(std::contiguous_iterator auto begin, std::contiguous_iterator auto end) -> parse_window;
この関数の実行結果を取得し、パースの成否をチェックして継続のアクションを記述するには、例えば次のように書くことができます
if (auto [first, last] = parse(begin(), end()); first != last) { // interpret [first, last) into a value }
この時、構造化束縛宣言を条件式として使用できるとより記述が単純化されます。
// 現在できない if (auto [first, last] = parse(begin(), end())) { // interpret [first, last) into a value }
この提案は、これをできるようにしようとするものです。
この提案による条件式における構造化束縛はオブジェクトの分解のみを担っており、条件判定に使用されるのは構文上からは隠蔽されている右辺の結果オブジェクトそのものです。従って、先ほどの例ではパース結果を表すparse_window
型がパース成否を表現できるようにしておく必要があります。
struct parse_window { char const *first; char const *last; // パース成否テストを追加 explicit operator bool() const noexcept { return first != last; } };
// この提案の後、次の2つのif文は同じ意味になる if (auto [first, last] = parse(begin(), end()); first != last) { // interpret [first, last) into a value } if (auto [first, last] = parse(begin(), end())) { // interpret [first, last) into a value }
提案より、その他の例。
C++26のstd::to_chars
の例
// この提案がない場合の書き方 if (auto result = std::to_chars(p, last, 42)) { auto [to, _] = result; auto s = std::string(p, to); // 変換した数字文字列 ... } else { auto [_, ec] = result; std::cout << ec << '\n'; }
// この提案の場合の書き方 if (auto [to, ec] = std::to_chars(p, last, 42)) { auto s = std::string(p, to); // 変換した数字文字列 ... } else { std::cout << ec << '\n'; }
C++26では、to_chars_result
にbool
変換演算子が追加されることにより、先ほどの例と同様にこの提案の恩恵を受けることができます。これはstd::from_chars
も同様です。
if (int v; auto [ptr, ec] = std::from_chars(p, last, v)) { auto s = std::string(ptr, last); // 変換に使用されなかった残りの部分の文字列 ... } else { std::cout << ec << '\n'; }
何か数学的な反復ソルバの例。
反復ソルバは主要な処理ステップをループを回して実行し、特定の条件が満たされるまでこのループを継続することで問題を解こうとします。
while (true) { auto [Ap, bp, x, y] = solve(...); // 最適解が得られていたらループを抜ける if (is_optimal(x)) // scan the x vector { break; } ... }
この時、is_optimal(x)
は線形かそれより悪いアルゴリズムが使用される(あるいは使用せざるを得ない)場合があります。一方で、ソルバステップ(solve()
)においては答えが最適かどうかを認識しその情報をキャッシュすることができる場合があります。その場合に戻り値からそれを直接取得できればコードが簡潔かつ効率的になります。
while (true) { // 最適解が得られていたらループを抜ける if (auto [Ap, bp, x, y] = solve(...)) { break; } ... }
これらの例に共通しているのは、ある処理の成否を保持しているのはその戻り値の一部または全部のコンポーネントであり、そのチェックの方法は型によって様々かつ自明ではないということです。この場合に、処理の成否をその処理の結果型そのものに焼き付けることでそのような煩雑なチェックコードを削減することができます。しかし、処理の結果として必要なのが結果オブジェクトのサブコンポーネントである場合、それを行おうとすると今度は余分な中間オブジェクトを導入しなければなりません(上記to_chars()
の例のように)。
現状の構文では結果の分解と結果の成否チェックを同時に実行することができませんが、この提案による条件式における構造化束縛宣言を用いるとそれを同時に記述することができるようになります。またこれによって、何か処理結果を表す型を書く際に、その処理の成否のテスト方法を型に埋め込みユーザーはその方法について無知なままで結果を簡易に利用できるようにする(上記例のような)コーディングスタイルを促進します。
- P2497R0 Testing for success or failure of charconv functions - [C++]WG21月次提案文書を眺める(2023年02月)
- P0963 進行状況
P1068R8 Vector API for random number generation
<random>
にある既存の分布生成器にイテレータ範囲を乱数で初期化するAPIを追加する提案。
以前の記事を参照
- P1068R4 : Vector API for random number generation - [C++]WG21月次提案文書を眺める(2020年7月)
- P1068R5 Vector API for random number generation - [C++]WG21月次提案文書を眺める(2021年05月)
- P1068R6 Vector API for random number generation - [C++]WG21月次提案文書を眺める(2022年10月)
- P1068R7 Vector API for random number generation - [C++]WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は、std::span
ベースのカスタマイズの許可、generate_random
のイテレータ版を追加、design-considerationsセクションの拡充などです。
P1967R11 #embed
- a simple, scannable preprocessor-based resource acquisition method
コンパイル時(プリプロセス時)にバイナリデータをインクルードするためのプリプロセッシングディレクティブ#embed
の提案。
以前の記事を参照
- P1967R3
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2021年04月) - P1967R4
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2021年06月) - P1967R5
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2021年04月) - P1967R6
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2022年05月) - P1967R7
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2022年06月) - P1967R8
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2022年07月) - P1967R9
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2022年10月) - P1967R10
#embed
- a simple, scannable preprocessor-based resource acquisition method - [C++]WG21月次提案文書を眺める(2023年01月)
このリビジョンでの変更は、EWGでの投票結果の追記、CWGレビューに伴う提案する文言の修正などです。
この提案は現在CWGのレビュー中です。
P2407R5 Freestanding Library: Partial Classes
一部の有用な標準ライブラリのクラス型をフリースタンディング処理系で使用可能とする提案。
以前の記事を参照
- P2407R0 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2021年07月)
- P2407R1 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2021年11月)
- P2407R2 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2023年01月)
- P2407R3 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2023年04月)
- P2407R4 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2023年07月)
このリビジョンでの変更は
- 名前空間スコープで定義されないものについて何がフリースタンディング指定されるのかを明確化
- freestanding.itemの番号をふり直した
- クラスメンバの全てがフリースタンディングとなる必要がないように要件を修正
- 言葉として、関数の定義よりもエンティティを使用する
- フリースタンディングのコメント文法を簡素化
- フリースタンディングからホストへの移行に関するメモを調整
- 文言の根拠となるメモなどの追加
std::variant
の指定において、get_if
の代わりに説明専用のGET
関数を使用するthis
以外のオブジェクトにアクセスする際に、string_view
の説明用インターフェースではなくpublic
インターフェースを使用する
などです。
この提案はLWGのレビューを通過し、次の全体会議で投票にかけられることが決まっています。
P2521R5 Contract support -- Record of SG21 consensus
C++26の契約プログラミングのMVP仕様に対する決定などを追跡するための文書。
以前の記事を参照
- P2521R1 Contract support -- Working Paper - WG21月次提案文書を眺める(2022年02月)
- P2521R2 Contract support - Working Paper - WG21月次提案文書を眺める(2022年03月)
- P2521R3 Contract support -- Record of SG21 consensus - WG21月次提案文書を眺める(2023年02月)
- P2521R4 Contract support -- Record of SG21 consensus - WG21月次提案文書を眺める(2023年07月)
このリビジョンでの変更は
- オーバーライドする関数は事前条件と事後条件を持てないが、オーバライドされる関数の事前条件と事後条件が適用される
- 構文の選択に関する議論を短縮
- 未解決問題のリストを更新
などです。
P2728R6 Unicode in the Library, Part 1: UTF Transcoding
標準ライブラリにユニコード文字列の相互変換サポートを追加する提案。
以前の記事を参照
- P2728R0 Unicode in the Library, Part 1: UTF Transcoding - WG21月次提案文書を眺める(2023年01月)
- P2728R3 Unicode in the Library, Part 1: UTF Transcoding - WG21月次提案文書を眺める(2023年05月)
- P2728R5 Unicode in the Library, Part 1: UTF Transcoding - WG21月次提案文書を眺める(2023年07月)
このリビジョンでの変更は
null_sentinel_t
の比較演算子の複雑な制約を単純化ranges::project_view
を導入し、それによってcharN_views
を実装するように変更utfN_views
を個別クラスではなくエイリアスに変更
などです。
P2746R3 Deprecate and Replace Fenv Rounding Modes
浮動小数点環境の丸めモード指定関数std::fesetround()
を非推奨化して置き換える提案。
以前の記事を参照
- P2746R0 Deprecate and Replace Fenv Rounding Modes - WG21月次提案文書を眺める(2023年01月)
- P2746R1 Deprecate and Replace Fenv Rounding Modes - WG21月次提案文書を眺める(2023年04月)
- P2746R2 Deprecate and Replace Fenv Rounding Modes - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- Varna会議でのコメントを追記
- Cの予約名との衝突を避けるため、
cr_xxx()
という関数名をcr::xxx()
のようにcr
名前空間に配置 - flush-to-zero問題への対処/回避
cr::cast()
の指定を修正- 実装が複雑になるのを回避するため、一部の関数で
constexpr
を避ける cr::make()
に、欠落していた丸モード引数を追加consteval
からconstexpr
に変更
などです。
P2795R3 Erroneous behaviour for uninitialized reads
未初期化変数の読み取りに関して、Erroneous Behaviourという振る舞いの規定を追加する提案。
以前の記事を参照
このリビジョンでの変更は、
- 振る舞いを修正する対象を“object with automatic storage duration”から“non-static local variable”に変更
- 一時オブジェクトと関数引数を除外することを意図している
- オプトアウトのための
[[indeterminate]]
属性を追加
などです。
[[indeterminate]]
属性は変数の定義に対して指定するもので、これが指定されたローカル変数が初期化子を持たない場合、現在と同様に初期化されません。
void f(int); void g() { int x [[indeterminate]], y; // xは未初期化、yは実装定義の値で初期化 f(y); // OK、yの読み取りはerroneous behaviour f(x); // UB、xの読み取りはundefined behavior }
P2821R4 span.at()
std::span
に.at()
メンバ関数を追加する提案。
以前の記事を参照
- P2821R0
span.at()
- WG21月次提案文書を眺める(2023年04月) - P2821R1
span.at()
- WG21月次提案文書を眺める(2023年05月) - P2821R2
span.at()
- WG21月次提案文書を眺める(2023年07月)
このリビジョンでの変更は、提案する文言を追加したことです。
この提案はLEWGのレビューを終えて、LWGに転送するためのLEWGでの投票待ちをしています。
P2833R1 Freestanding Library: inout
expected
span
C++23のライブラリ機能の一部をFreestanding指定する提案。
以前の記事を参照
このリビジョンでの変更は、P2407R5の変更を適用、機能テストマクロをフリースタンディング指定、P2821とのコンフリクトについて追記、などです。
この提案はLEWGのレビューを終えて、LWGに転送するためのLEWGでの投票待ちをしています。どうやら、C++23へのDRとなるようです。
P2845R2 Formatting of std::filesystem::path
std::filesystem::path
をstd::format()
でフォーマット可能にする提案。
以前の記事を参照
- P2845R0 Formatting of
std::filesystem::path
- WG21月次提案文書を眺める(2023年05月) - P2845R1 Formatting of
std::filesystem::path
- WG21月次提案文書を眺める(2023年07月)
このリビジョンでの変更は、デバッグ出力(?
オプション)を提供するようにしたことです。
P2863R1 Review Annex D for C++26
現在非推奨とマークされている機能について、C++26で削除/復帰を検討する提案。
以前の記事を参照
このリビジョンでの変更は
- D.29(
filesystem::u8path
)に何もしないことの根拠を追記 - Issueへのリンクを修正
- ベースとなるドラフトを更新
- 進捗状況について追記
などです。
個別の提案に分かれていないものとして、static constexpr
なクラスのメンバ変数に対する定義の再宣言の非推奨を解除することにEWGで合意がなされ、別の提案で進められることになりました。
P2864R1 Remove Deprecated Arithmetic Conversion on Enumerations From C++26
C++20の一貫比較仕様に伴って非推奨とされた、列挙値から算術型への暗黙変換を削除する提案。
以前の記事を参照
このリビジョンでの変更は、文言の修正や改善などです。
この提案の担当範囲については、EWGのレビューで削除することに合意が取れなかったため作業は中止されるようです。
P2865R2 Remove Deprecated Array Comparisons from C++26
C++20の一貫比較仕様に伴って非推奨とされた、配列間の比較を削除する提案。
以前の記事を参照
- P2865R0 Remove Deprecated Array Comparisons from C++26 - WG21月次提案文書を眺める(2023年05月)
- P2865R1 Remove Deprecated Array Comparisons from C++26 - WG21月次提案文書を眺める(2023年07月)
このリビジョンでの変更は、文言の修正やCWGレビューについて追記したことなどです。
P2868R1 Remove Deprecated std::allocator
Typedef From C++26
std::allocator
にある非推奨化された入れ子型定義を削除する提案。
以前の記事を参照
このリビジョンでの変更は
- LEWGのレビューで明らかになった懸念について追記
- ゾンビ名セクションを変更しないことを確認
- Annex Cの文言を提供
などです。
P2869R1 Remove Deprecated shared_ptr
Atomic Access APIs From C++26
C++20で非推奨とされた、std::shared_ptr
のアトミックフリー関数を削除する提案。
以前の記事を参照
このリビジョンでの変更は、SG1やLEWGのレビューを受けての文言の修正などです。
P2870R1 Remove basic_string::reserve()
From C++26
C++20で非推奨とされたstd::string::reserve()
をC++26に向けて削除する提案。
以前の記事を参照
このリビジョンでの変更は
- 異なるバージョン間でこの関数を使用するリスクについて追記
- 古いコードからの移行について追記
- ゾンビ名セクションを変更しないことを確認
- Annex Cの文言を提供
などです。
P2871R1 Remove Deprecated Unicode Conversion Facets From C++26
C++17で非推奨とされた<codecvt>
ヘッダをC++26で削除する提案。
以前の記事を参照
このリビジョンでの変更は、提案する文言の修正などです。
P2875R1 Undeprecate polymorphic_allocator::destroy
For C++26
C++20で非推奨とされたpolymorphic_allocator::destroy
の非推奨化を解除する提案。
以前の記事を参照
このリビジョンでの変更は、ベースとなるWDの更新と文言の調整などです。
P2878R5 Reference checking
プログラマが明示的に関数の戻り値に関するライフタイム注釈を行えるようにする提案。
以前の記事を参照
このリビジョンでの変更は、do
式とパターンマッチングにおけるダングリングの問題に対する解決策の修正、std::thread/std::jthread
に対する6種目のチェックの提案、などです。
P2885R1 Requirements for a Contracts syntax
C++契約プログラミング機能の構文の選択のための評価基準や要件をまとめる文書。
以前の記事を参照
このリビジョンでの変更は
- [func.mix]と[future.abbrev]に新しい要件を追加
- "Accessibility" [basic.access]を"Teachability" [basic.teach]へ変更
- [basic.lang]と[compat.cpp]の要件を単一の[basic.cpp]にマージ
- [func.retval]を満たすために提案された2つのアプローチを、個別のより具体的な要件である[func.retval.predef]と[func.retval.userdef]の2つに分解
- [func.pos.prepost]と[func.pos.assert]から、位置と名前探索に関する基本要件を抽出し[func.pos]にまとめた
- 要件[func.pos.prepost]と[func.pos.assert]をより具体的にした
- 事前/事後条件はパラメータ宣言の後に置く必要があり、アサーションはCのアサートがかける場所ならどこでも書けるようにする
- [compat.back], [compat.impl]と[future.reuse]にそれぞれ根拠を追記
- よりバランスの取れた表現となるように、[basic.aesthetic]と[basic.brief]を修正
- [compat.c]の意図を明確化
- [func.pos]において、属性構文の事前/事後条件の位置についての間違いを修正
- [func.pos.prepost]に、配列ポインタを返す関数の曖昧な位置と名前探索の例を追加
- [func.pos.prepost]と[func.mix]に違反する構文のアイデアとして、ピアノ構文についての言及を追記
- [future.prim]に違反する属性構文の例を追加
- [future.captures]を少し書き直して、仮引数名のキャプチャとそれ以外のもののキャプチャの違い、及び初期化キャプチャの許可とラムダのようなあらゆる種類のキャプチャ許可の違いを説明
- P2935R0で説明されている属性構文の様々な拡張および設計の代替案について、文書全体に追記
- 一次情報と二次情報について説明する段落を概要に追加
- 将来の進化に対する要求の妥当性の違いについて言及
- 要求の優先順位を決定し、それらを客観的/主観的なものとして分類するために、SG21で実施予定の電子投票に言及した方法論のセクションを書き直し、個々の要件の説明からそのような優先順位や分類を削除した
などです。
P2890R0 Contracts on lambdas
ラムダ式に対する契約条件指定ができるようにする提案。
現在進行中の契約プログラミングに関する議論においては、通常の関数に対して契約を行うことを主眼に議論されています。同様にラムダ式に対しても行えることが期待されますが、現在のところラムダでに対してどのように動作するのかはあまり考慮されていないようです。
この提案は、契約条件指定がラムダ式においても通常の関数と同じように動作し、なおかつキャプチャをどのように処理するかを提案するものです。
現在のContracts MVP仕様(P2388R4)においてはラムダ式における契約条件指定はその名前解決の問題から未解決の問題とされ、P2036にその解決を委ねMVPとしてはそれを待ってはいません。しかし、P2036はR3が既にC++23に採択されているため、その問題は解決済みでした。
この提案では、ラムダ式における契約条件式内の名前探索はP2036で提案されている後置戻り値型におけるそれと同様にすることを提案しています。すなわち、そのコンテキストで表れている名前はまずキャプチャ名が考慮されます。
int i = 0; double j = 42.0; ... auto counter = [j=i]() mutable [[pre: j >= 0]] { // ^ return j++; };
(ここでが契約構文として属性構文を使用していますが、これは仮のもので、この提案の内容は構文とは独立しています)
この例における事前条件式内のj
は外側で宣言されているdouble
型の変数j
ではなく、そのラムダにおいてキャプチャされている変数j
(int
型)として扱われます。
この名前解決以外のところでは通常関数に対する契約条件指定と同じ振る舞いとなります。特に、現れる名前が常にODR-usedとなるという点も現在の契約仕様及び言語の他の部分([[assume]]
など)と同様とすることを提案しています。
P2894R0 Constant evaluation of Contracts
定数式においても契約チェックを有効化する提案。
現在進行中の契約プログラミングに関する議論においては、実行時の関数に対して契約を行うことを主眼に議論されています。そのため、定数式において契約条件チェックするかどうか、それがどのようなセマンティクスを持つのか、などはあまり議論されておらず、方向性や一致した見解は確立されていません。
現在のC++はコンパイル時プログラミングがかなり強力かつ一般的なものとなっており、実行時に動作するコードの多くはコンパイル時にも動作し、契約プログラミングに関しても同様であることが自然と期待されます。この提案はそのために現在の契約プログラミング仕様の下で定数式における契約条件チェック周りのセマンティクスについて議論し提案するものです。
現在の契約プログラミング仕様は、P2877R0の採択によってビルドモードの概念が削除され、契約アノテーションのセマンティクスは実装定義となりコンパイル時には認識できないようにされています。そのため、C++のコードの一部がill-formedであるかを契約プログラミングのセマンティクス(実行時にチェックされるかどうか)に依存させることはできなくなっています。したがって、定数評価中はP2877R0で示されている3つのセマンティクス(ignore, observe, enforce)のいずれかによって実行されているかを選択することができません。
契約アノテーションの定数評価時のセマンティクスについて指定が必要なのは次のどちらかの場合についてです
- 契約条件式はコア定数式ではない
- 契約条件式はコア定数式だが、
false
に評価された(契約が破られた)
この2つの場合についてそれぞれ、セマンティクスを指定する必要があります。
1つ目のケースについてはまず、P2448R2がC++23に適用されたため、constexpr/consteval
関数がコンパイル時に呼び出されなければそこに指定されている契約条件について何かをする必要はありません。
int pred(); // constexprではない述語 constexpr int f() [[ pre: pred() ]]; // OK; コンパイル時に呼ばれない consteval int g() [[ pre: pred() ]]; // OK: 使用されない int main() { return f(); // f()はコンパイル時に呼び出されない }
考慮すべきは、この場合にf()/g()
が定数式で呼ばれ、契約条件式を評価しようとしてpred()
の呼び出しで何が起こるかです。
int pred(); // constexprではない述語 int f() constexpr [[ pre: pred() ]]; // OK; コンパイル時に呼ばれない int main() { std::array<int, f()> a; // f()が定数式で呼び出される ... }
この場合の振る舞いには次の4つのオプションが考えられます
- ill-formed
- ill-formed、ただし診断不要
- 契約条件式は無視される(定数評価中のみ)
- 契約条件式は無視するが、警告を発することを推奨する
さらに、このうちどのオプションを選択するかは実装定義とすることもできます。
2つ目のケースの場合については、例えば次のプログラムはどのように動作すべきでしょうか?
int f(int i) constexpr [[ pre: i > 0 ]]; int main() { std::array<int, f(0)> a; // 契約条件を満たさない定数式中の呼び出し ... }
実行時の場合は契約条件がtrue
に評価されない場合とは多岐にわたります
false
に評価された- 例外をスローした
longjmp
を呼び出した- プログラムを終了させた
- 未定義動作に陥った
実行時において契約違反となるのは最初の2つの場合のみです。また、そのセマンティクスもignore, observe, enforceの3つのいずれかが実装定義で選択(指定)され、それによって契約条件は無視されるか(ignore)、破られて違反ハンドラが呼ばれた後どうなるか(observe, enforce)、が変化します。
定数評価中はこの点が大きく異なり、定数評価中は契約アノテーションのセマンティクスを選択できず(どれが選択されているかはわからず)observe, enforceを区別する意味がありません。無視されずにtrue
と評価されなかった場合はfalse
に評価される以外に選択肢がありません。定数評価中は例外を投げたりlongjmp
を呼び出したりできず、それが行われる場合はコア定数式ではないためそのセマンティクスの規定はケース1の領分となります。
そのため、このケース2のセマンティクスの指定は、契約条件式がコア定数式でありfalse
に評価された場合についてのみ考慮するだけですみます。ケース1と同様に、ここでも4つの選択肢があります
- ill-formed
- ill-formed、ただし診断不要
- 契約条件式は無視される(定数評価中のみ)
- 契約条件式は無視するが、警告を発することを推奨する
ここでも、このうちどのオプションを選択するかは実装定義とすることもできます。
これらのことと設計原則(正しさ、柔軟さ、教えやすさ、コンパイル時間)を前提に、提案では3つのオプションを提示しています
- オプションA : どちらもill-formedとする
- オプションB : どちらも契約条件式を無視する
- オプションC : どちらの場合のセマンティクスも実装定義とする
契約プログラミングの場合は正しさが最も重要な設計目標であるため、オプションAが最も望ましい選択肢です。しかし、それによってコンパイル時間の増大を許容できなくなったユーザーがマクロにラップしてその有効性をスイッチするようなことをし始めると、契約機能の採用に影響を及ぼす可能性があります。なぜなら、オプションAでは契約条件がコンパイル時に評価されないが実行時には評価される、ようなことを許可しないためです。
オプションBも正しさの観点から適切ではありません。コンパイル時間のために正しさを犠牲にし、結果得られるプログラムが未定義の振る舞いをするようになってしまったら意味がありません。標準は少なくともこのようなケースを診断できるべきです。
したがって、最も推奨されるのはオプションCとなります。コンパイラ引数等のスイッチが増えるのはやや残念な点ですが、これによって契約プログラミングを使用する全てのユーザーに契約を有効に使用するための選択肢を与えることができます。例えば、このオプションでは契約条件がコンパイル時に評価されないが実行時には評価される、といったことが許容されます。
- P2877R0 Contract Build Modes and Semantics - [C++]WG21月次提案文書を眺める(2023年07月)
- P2448R0 Relaxing some constexpr restrictions - [C++]WG21月次提案文書を眺める(2021年10月)
- P2894 進行状況
P2896R0 Outstanding design questions for the Contracts MVP
契約プログラミングに関する未解決の問題について収集し、解決を促すための提案。
C++における現在の契約プログラミングに関する議論は、MVPと呼ばれる最小仕様を確立し、それをC++26に導入することを目指しています。そのタスクの残りの主要な論点は契約構文に関するものですが、それ以外にも小さいながらも解決すべき問題がいくつか残されています。
この提案は、それらの未解決の問題(設計上の疑問点)を収集してリスト化し、MVP仕様が確立される前に何らかの回答(SG21における投票や個別の提案の採択など)を求めるものです。
- 異なる翻訳単位の最初の宣言における契約について
- 未解決、提案が必要
- 現在のMVPでは、
f
が異なる翻訳単位で宣言されている場合、その契約は同一(identical)でなけれならない(そうでない場合診断不用のill-formed)とされているが、同一(identical)の意味が定義されていない。この定義が必要 - 選択肢
- 同一(identical)の意味を定義し、これが実装可能であることを確認する
- 異なる翻訳単位の同じ関数の2つの宣言について、両方に契約がなされている場合をill-formed(診断不用)として問題を回避する。ただしこれは実用的ではない
- オーバーライドする関数とされる関数の契約について
- 未解決、提案が必要
- 現在のMVPでは、オーバーライドする関数はされる関数の契約を継承し、追加の契約を行えない。これについて異論があり、どうするかを選択する必要がある。
- 選択肢
- なにもしない(現在のMVPのまま)
- MVPの制限を強め、オーバーライドする関数もされる関数も契約を行えないようにする
- 継承された契約をオーバーライドする機能などの仮想関数に対するより柔軟なソリューションを考案し、MVPを緩和する
- ラムダ式に対する契約と暗黙キャプチャについて
- コルーチンにおける契約
- 定数式における契約について
- 未解決、P2894R0で提案済
- 定数評価中に契約条件は評価されるのか、どういうセマンティクスを持つのかが未解決。特に、契約条件式はコア定数式ではない場合と、契約条件式はコア定数式だが
false
に評価された場合にどうなるのかが問題。 - 選択肢(2つの場合のどちらについても)
- ill-formed
- ill-formed、ただし診断不要
- 契約条件式は無視される(定数評価中のみ)
- 契約条件式は無視するが、警告を発することを推奨する
- トリビアルな特殊メンバ関数に対する契約について
この提案は、これらの問題の解決を2024年春の東京における全体会議までに解決するべきとしています。
P2905R2 Runtime format strings
std::make_format_args()
が左辺値でフォーマット対象の値を受け取るようにする提案。
以前の記事を参照
このリビジョンでの変更は、R1に対するLEWGの投票結果を追記したことです。
P2909R0 Dude, where's my char?
std::format()
のchar
を整数値としてフォーマットする際の挙動を改善する提案。
std::format()
ではchar
の値をd
やx
オプションによって整数値として出力することができます。ただ、この出力はint
に暗黙変換して出力する形になっており、char
の符号が実装定義であることを考慮していませんでした。
import std; int main() { for (char c : std::string("🤷")) { std::print("\\x{:02x}", c); } }
リテラルエンコーディングがUTF-8だとすると、この出力はchar
が符号付きか否かによって次のどちらかになります
\xf0\x9f\xa4\xb7 \x-10\x-61\x-5c\x-49
例えば、ARM環境は多くの場合char
が符号付きとなります。
std::format()
の暗黙的な設計意図として、同じ整数型と同じIEEE754浮動小数点数型についてプラットフォーム間で一貫した出力をする、というものがあります。しかし、現在の仕様だとchar
がそのプロパティを破っています。
また、char
をstd::format()
で出力するときは、元のテキストの部分テキストとして出力するか、そのビットパターンを出力するために使用されます。後者の場合、それは符号なし整数を通常意図しているはずで、プラットフォームによっては符号付き整数値として出力されることは意図しないはずであり、10進以外で出力している場合は特にそうでしょう。
この問題は{fmt}ライブラリにおいて報告され、既に修正済みです。この提案は、この修正をstd::format()
にも適用しようとするものです。
提案では、char
の値をb, B, d, o, x, X
オプションによって出力する際は対応する符号なし整数型(つまり、unsigned char
)にキャストしてからフォーマットする、のように指定することでこの問題の解決を図っています。
この修正は実行時の振る舞いを静かに変化させる破壊的変更となりますが、単一のchar
値をb, B, d, o, x, X
オプションによって出力する場合、かつchar
が符号付き整数型であるプラットフォームにおいてのみ影響があります。変更が{fmt}ライブラリに適用され出荷されて以降この修正が問題となった報告はなく、std::format()
の利用率はそれよりさらに低いため破壊的変更の影響はかなり小さいと考えられます。
LEWGにおける投票では、この修正をC++23に対するDRとすることに合意がとれています。
P2933R0 std::simd
overloads for <bit>
header
<bit>
にあるビット演算を行う関数について、std::simd
向けのオーバーロードを追加する提案。
std::simd
はデータ並列型として、通常のスカラ型とほぼ同様に使用してハードウェアのSIMD演算を活用できるようにすることを意図したクラス型です。そのため、演算子オーバーロードや関数オーバーロードによって通常の四則演算や数学関数などを使用可能なようになっています。
この提案は、現在そこに欠けている<bit>
のビット演算系関数のオーバーロードを追加しようとするものです。
この提案の対象は、C++23時点で<bit>
ヘッダにあるもののうちstd::endian
とstd::bit_cast
を除いた残りの関数です。追加されるオーバーロードの制約は元の関数のものをstd::simd
の要素型に対して要求し、効果はstd::simd
の値の要素ごとに適用され、結果は入力と同じstd::simd<T, ABI>
型の値として返されます。
ただし、countl_zero()
やbit_width()
などのようにビットの状態をクエリする系の関数の戻り値型は通常のものはint
となっていますが、std::simd
で同じようにすると結果の値のビット幅とストレージサイズが変化しパフォーマンスに影響を与える可能性があるため、これらの関数については入力の要素型と同じ幅の符号付き整数型を要素型とするstd::simd
値を返すようにされています。
P2935R0 An Attribute-Like Syntax for Contracts
C++契約プログラミングのための構文として属性構文を推奨する提案。
C++における現在の契約プログラミングに関する議論は、MVPと呼ばれる最小仕様を確立し、それをC++26に導入することを目指しています。意味論面に関しては小さい問題がいくつか残されていますがそれは本質的な問題ではなく、残った大きな決定事項は契約構文に関するものです。
現在の契約プログラミングのための構文としては、C++20で一旦採用された際の属性構文の他、ラムダ式likeな構文、専用の区切り記号を利用するものなどの候補があります。この提案は、そのうちC++20で採用されたものとほぼ同様の属性構文によるものを提案しています。
提案されている構文の文法は次のようなものです
contract-checking-annotation : precondition-specifier postcondition-specifier assertion-specifier precondition-specifier : [ [ pre : conditional-expression ] ] postcondition-specifier : [ [ post postcondition-return-value-specifier(opt) : conditional-expression ] ] postcondition-return-value-specifier : identifier ( identifier ) assertion-specifier : [ [ assert : conditional-expression ] ] attribute-specifier : [ [ attribute-using-prefix(opt) attribute-list ] ] alignment-specifier precondition-specifier postcondition-specifier assertion-statement : assertion-specifier ;
よりまとめると、契約アノテーションの構文は次のような形式をしています
contract-checking-annotation [ [ contract-kind metadata-sequence : evaluable-code ] ]
contract-kind
にはpre post assert
のいずれかが指定でき、metadata-sequence
には事後条件における戻り値のキャプチャのみが置けます。evaluable-code
は文脈に応じてbool
に変換される通常のC++の式、すなわちconditional-expression
を置くことができます。
この構文の利用例
int f(const int x, int y) [[ pre : y > 0 ]] [[ post : fstate() == x ]] // 事後条件から参照される仮引数名はconstでなければならない [[ post r : r > x ]] // postの後に変数名を指定することで戻り値を参照する [[ post (r) : r > x ]] // 戻り値名は()で括っても良い { [[ assert : x > 0 ]]; // Assertions form a complete statement. return x; }
事前条件と事後条件は関数ではなく関数の型に関連する属性が配置されるのと同じ場所に配置され、noexcept
の後かつ後置戻り値型の前に配置されます。
struct S1 { auto f() const & noexcept [[ pre : true ]] -> int; virtual void g() [[ pre : true ]] final = 0; template <typename T> void h() [[ pre : true ]] requires true; };
ラムダ式の場合も同様になります。
auto w = [] [[pre: true]] { return 6; }; auto x = [] (int x) [[pre: true]] { return 7; }; auto y = [] (int x) [[pre: true]] -> int { return 8; };
事前条件と事後条件の数や順序に制限はなく、好きな順番で好きな数追加できます。
std::pair<double,double> clamp2d(double x, double y, const double minx, const double maxx, const double miny, const double maxy) // Check the x-dimension range. [[ pre : minx <= maxx ]] [[ post r : r.first >= minx && r.second <= maxx ]] // Check the y-dimension range. [[ pre : miny <= maxy ]] [[ post r : r.second >= miny && r.second <= maxy ]];
assertion-statement
はそれ単体で1つの文を形成し、ブロックスコープの内側でのみ使用可能となります。ただし、assertion-specifier
そのものにはそうした制限はなく、将来的にvoid
型の式として式に付随させることを許可することを妨げません。
struct S2 { int d_x; S2(int x) : d-x( [[ assert : x > 0 ]], x ) // error、ただし将来的に許可する事は可能 {} };
P2944R1 Comparisons for reference_wrapper
reference_wrapper
に比較演算子を追加する提案。
以前の記事を参照
このリビジョンでの変更は、提案する文言の修正のみです。
P2951R2 Shadowing is good for safety
変数のシャドウィングを活用した、安全性向上のための言語機能の提案。
以前の記事を参照
このリビジョンでの変更は、5つ目の提案を追加したことです。
5つ目の提案は、2つ目の提案を範囲for
で特化させたもので、イテレート対象の範囲をその内部で暗黙的にconst
シャドウィングを行うものです。
#include <string> #include <vector> #include <optional> using namespace std; int main() { vector<string> vs{"1", "2", "3"}; [[checked]] for (auto &s : vs) { // 暗黙的にconstシャドウィングされる //const vector<string>& vs = vs; } // 代替構文 cfor (auto &s : vs) { //const vector<string>& vs = vs; } return 0; }
この例のvs
のようにイテレート対象の式が単純な変数名であればこの提案の実装には問題がありませんが、ここにはより複雑な式が来る可能性があります。その時に同じことをしようとすると、より複雑な組み合わせのパターンマッチングが必要になります。そのため、実装の選択肢は3段階のチェックに分かれます。
vs
のように単純な式(変数名)のみ自動シャドウィングするvs.member.function().member
のような複雑な式が、その範囲for
のスコープ内で非const
として使用されないようにする- 式の組み合わせがその範囲
for
のスコープ内で非const
として使用されないようにする
2と3の例
// 2の例 [[checked]] for (auto &s : vs.member.function().member) { // 非constで`vs.member.function().member`を使用できない vs.member.function().member.non_const_method(); // error } // 3の例 cfor (auto &s : vs.member.function().member) { auto first2 = vs.member; auto remainder = first2.function().member; // 非constで`vs.member.function().memberと同等のものを使用できない remainder.non_const_method(); // error }
P2952R0 auto& operator=(X&&) = default
default
な特殊メンバ関数の戻り値型をauto&
で宣言できるようにする提案。
現在のC++では、= default
は特定のシグネチャと戻り値型を持つ関数にのみ使用できます。そのような関数の戻り値型は変更することができ、そのコンテキストから特定可能な型ですが、現在の規定では<=>
を除いて戻り値型に対してauto
を使用できません。
これにより、例えば代入演算子の実装において冗長な繰り返しが避けられません。
struct ForwardDiffView { ... ForwardDiffView(ForwardDiffView&&) = default; ForwardDiffView(const ForwardDiffView&) = default; ForwardDiffView& operator=(ForwardDiffView&&) = default; ForwardDiffView& operator=(const ForwardDiffView&) = default; ... };
また、比較演算子も<=>
とこの点において一貫していません。
auto operator<=>(const MyClass& rhs) const = default; // well-formed auto operator==(const MyClass& rhs) const = default; // ill-formed、boolでなければならない auto operator<(const MyClass& rhs) const = default; // ill-formed、boolでなければならない
これらのことはまた、default
ではない場合とも一貫していません。
auto& operator=(const MyClass& rhs) { i = rhs.i; return *this; } // well-formed auto& operator=(const MyClass& rhs) = default; // ill-formed、'MyClass&'でなければならない auto operator==(const MyClass& rhs) const { return i == rhs.i; } // well-formed auto operator==(const MyClass& rhs) const = default; // ill-formed、'bool'でなければならない
この提案は、これらの例のように現在行えない特殊メンバ関数の戻り値型におけるauto
の使用を許可しようとするものです。
この場合のauto
の示す型名は、特定の許可される型のセットを定義してそこから選択するのではなく、仮想のretrun
文を返す関数としての通常の戻り値型推論によって推定されます。
auto
戻り値型を持つdefault
な関数宣言は、その関数の種別によって次のような値を返す架空のreturn
文から推論された型を持ちます
bool
型のprvalue :operator==
、operator!=
、operator<
、operator>
、operator<=
、operator>=
- 共通の比較カテゴリ型
Q
型のprvalue :operator<=>
C
の左辺値 : クラス型/共用体C
のoperator=
その後、このように取得された戻り値型が標準で許可されている戻り値型と一致するかを調べ、一致するならwell-defined、一致しないならill-formedとなります。
コピー代入演算子の例
struct MyClass { auto& operator=(const MyClass&) = default; // Proposed OK: deduces MyClass& decltype(auto) operator=(const MyClass&) = default; // Proposed OK: deduces MyClass& auto&& operator=(const MyClass&) = default; // Proposed OK: deduces MyClass& const auto& operator=(const MyClass&) = default; // Still ill-formed: deduces const MyClass& auto operator=(const MyClass&) = default; // Still ill-formed: deduces MyClass auto* operator=(const MyClass&) = default; // Still ill-formed: deduction fails };
等値比較演算子の例
struct MyClass { auto operator==(const MyClass&) const = default; // Proposed OK: deduces bool decltype(auto) operator==(const MyClass&) const = default; // Proposed OK: deduces bool auto&& operator==(const MyClass&) const = default; // Still ill-formed: deduces bool&& auto& operator==(const MyClass&) const = default; // Still ill-formed: deduction fails };
また、提案ではこの変更に伴って発見された既存の特殊メンバ関数(特に== <=>
)の宣言の微妙な差異によるバグやベンダ間の差異について明確となるようにを修正することも提案しています。
P2953R0 Forbid defaulting operator=(X&&) &&
右辺値修飾されたdefault
代入演算子を禁止する提案。
明示的にdefault
で宣言される代入演算子は、その参照修飾(CV修飾は禁止されている)や戻り値型等の微妙な違いによる宣言が許可されており、それによって暗黙のdefault
宣言で導入されるものとは異なったシグネチャのdefault
代入演算子を宣言可能になっています。
その中でも最も意味がないと思われるのが、右辺値参照修飾されたdefault
代入演算子の宣言です。
struct S { // どちらも、今日可能なdefault宣言 S& operator=(const S&) && = default; S& operator=(S&&) && = default; };
これには次のような問題があります
- このような無意味な宣言の可能性は、C++の理解を困難にさせる
- このような宣言を許可するための標準の文言は、禁止する場合に比べて複雑になる
この提案では、この右辺値参照修飾されたdefault
代入演算子の宣言を禁止することを提案しています。
この提案による恩恵はまた、明示的オブジェクトパラメータとP2952(1つ前の節)を採用した場合のコーナーケースを潰すのにも役に立ちます
struct C { auto&& operator=(this C&& self, const C&) { return self; } // 現在: OK, 戻り値型はC&&が推論される // P2952採択後: OK, 戻り値型はC&&が推論される(変わらない) // この提案: OK, 戻り値型はC&&が推論される(変わらない) auto&& operator=(this C&& self, const C&) = default; // 現在: Ill-formed, 戻り値型にプレースホルダを使用できない // P2952採択後: OK, 戻り値型はC&が推論される // この提案: 削除される、オブジェクトパラメータはC&ではない };
この提案がない場合、この2つの同じ意味と思われる宣言はP2952の採択後に異なる型を推論するようになり、一見するとこの推論は矛盾しているように思えます。P2952とこの提案を同時に採択することによってこのコーナーケースを軽減することができます。
なお、この提案では該当する代入演算子をill-formedとするのではなく、既存の振る舞いに従って暗黙delete
とすることを提案しています。
struct S { // この提案の後、どちらもdeleteされる S& operator=(const S&) && = default; S& operator=(S&&) && = default; };
P2954R0 Contracts and virtual functions for the Contracts MVP
現在のC++契約プログラミング仕様に対して、仮想関数に関する契約についてを規定する提案。
仮想関数のオーバーライドに伴う契約の指定に関しては、EiffelにおいてAssertion Redeclaration ruleと呼ばれる規則が確立されています。それは
- 事前条件は、継承元と同じかより弱い(緩い)ものである必要がある
- 事後条件は、継承元と同じかより強い(厳しい)ものである必要がある
というものです。
C++20の契約仕様においてもこれをベースとして、事前条件と事後条件はオーバーライド元と先で同一となることを指定していました。
- オーバーライドする関数に契約を指定する場合、オーバーライドされる関数の契約指定と同じものを指定する
- 関数が複数の関数をオーバーライドする場合、オーバーライドされる関数は全て同じ契約指定を持つ必要がある
現在C++26へ向けて進行中の契約仕様(MVP)においては
- オーバーライドする関数には契約指定を行うことはできず、オーバーライドされる関数の契約指定を継承する
- (関数が複数の関数をオーバーライドする場合は同一)
となっており、少し厳しくなっています。
この点に関してはさまざまな意見があるようで、特に、契約指定の同一性(あるいは契約の強弱や広さの定義)をどう判断するのかや、複数の関数をオーバーライドする場合に基底の関数全てに同じ契約指定を要求することが正しいのかについて合意がとれてはいないようです。
この提案は、現在のMVP仕様をC++26に間に合わせるためにも、この点について次のようにすることを提案しています
- 関数をオーバーライドする仮想関数には契約指定を行えず、オーバーライドされる関数の契約指定を継承する
- 関数が複数の関数をオーバーライドする場合、オーバーライドされる関数は契約指定を持ってはならない
これは、適切であり将来の互換性に影響がない部分(オーバーライド後関数は元の関数と同一の契約を持つこと)を許可し、現在異論があり意見の一致を見ていない部分(契約条件の同一性の定義、複数の基底関数への契約の強制について)を不許可(ill-formed、要診断)にしようとするものです。これによって、C++26契約仕様としては契約を持つ仮想関数のオーバーライドを可能にしつつ、それはAssertion Redeclaration ruleに従ったものになります(契約条件は基底関数のコンテキストにおいて同一)。現在異論がある部分については、実装経験などを見ながら将来的に決定することを目指します。
例えば、オーバーライドする関数は基底クラスの関数とは全く異なるコンテキスト及びスコープで定義される可能性があるため、トークンの同一性は契約条件の同一性を意味しません。ODRの同一性に関しは実装経験が乏しく(一応、GCCの契約実装がこれをおこなっているらしい)、それがユーザーにとってどのような影響をもたらすのかは不透明です。これを診断不要のill-formedにしてしまうと、契約仕様の理解に混乱をもたらします。そのため、明確にill-formedとしておくことで問題を分離し、単純化した振る舞いをC++26に間に合わせることができます。
GCCの契約実装はODRベースで契約指定の同一性を判断するようですが、これは複数の関数をオーバーライドしている場合には行われないようで、その点に関しても合意や実装経験はありません。そのため、これもill-formedとしておくことで振る舞いを単純化し、将来のために設計スペースを空けておくことができます。
P2955R0 Safer Range Access
std::vector
等コンテナ向けの安全な要素アクセス関数の提案。
std::vector
を始めとするコンテナ(std::deque
やstd::string
、std::span
などなど)は要素アクセスが安全であるとはいえません。.at()
以外のアクセス方法は範囲外アクセスをチェックしておらず、.at()
の利用率は低いため要素アクセスの大部分は範囲外参照チェックなしで行われています。
また、.at()
も戻り値が要素への直接の参照であるため、参照無効化・ダングリング参照の危険性を孕んでいます。
この提案は、値セマンティクスに基づく関数を提供することで、範囲外アクセスやダングリング参照によるエラーを低減させることを目指すものです。
現在のC++の要素アクセスに共通する課題として、次のようなものがあります
- C++の添字演算子は参照セマンティクスを持つ傾向にある
- 値の取得と設定それぞれに特化した専用の演算子がないためと考えられる
- C++の添字演算子はインデックスの一部ではない追加の引数をサポートしない
- 例えば
std::source_location
- 例えば
std::optional, std::variant, std::expected
は参照をサポートしない
この課題を回避するために、この提案では演算子やプロクシオブジェクトを使用する代わりにメンバ関数を使用しています。
まず追加するのは、与えられたインデックスが有効化どうかを判断する.test()
関数です。
[[nodiscard]] [[safe]] constexpr bool test( std::vector<T>::size_type pos ) const;
この関数は安全に実装できます([[safe]]
は新規属性ではなく、この提案内において安全/安全ではないことを示すマーカーです)。
次に追加する2つの関数は安全なものではありませんが、この提案のメインである安全な関数群を提供するためのベースとなるものです
[[nodiscard]] [[unsafe(reason=["range", "dangling_reference", "reference_invalidation"])]] constexpr reference get_reference( size_type pos ) noexcept; [[nodiscard]] [[unsafe(reason=["range", "dangling_reference", "reference_invalidation"])]] constexpr const_reference get_reference( size_type pos ) const noexcept;
ここでの[[unsafe]]
も[[safe]]
同様に説明のためのマーカーです。これらの関数は指定されたインデックスに対応する要素の参照を取得するものです。
これらをベースとした安全な関数群は、次のような設計理念によって分類されます
[], at(), front(), back()
等範囲アクセス関数に対しては、ゲッター(値を受け取る)とセッター(値を返す)とに分離する- 分離されたゲッターとセッター関数ごとに、エラーを処理する方法に応じたバリアントが追加される
- 例外送出
- デフォルト値を返す
std::optional
を返す- 終了する
- 何もしない(つまり、無効)
- 追加する
front()/back()
に対応する関数は例えば
取得/設定 | エラー処理の方法 | front() |
back() |
---|---|---|---|
get |
get_value |
get_front_value |
get_back_value |
get |
get_optional |
get_front_optional |
get_back_optional |
get |
get_or_terminate |
get_front_or_terminate |
get_back_or_terminate |
set |
set_value |
set_front_value |
set_back_value |
set |
set_or_terminate |
set_front_or_terminate |
set_back_or_terminate |
set |
set_and_crop |
set_front_and_crop |
set_back_and_crop |
set |
set_and_grow |
set_front_and_grow |
set_back_and_grow |
ゲッター関数は何も受け取らず値を返し、セッター関数は値を受け取り何も返しません。
要素を引き当てその参照に対して何かをしたい(値の更新やメンバ呼び出しなど)場合のために、ゲッター/セッターの分離はtransform/visit
に置き換えられ、対応する関数が追加されます
取得/設定 | エラー処理の方法 | front() |
back() |
---|---|---|---|
transform |
transform_value |
transform_front_value |
transform_back_value |
transform |
transform_optional |
transform_front_optional |
transform_back_optional |
transform |
transform_or_terminate |
transform_front_or_terminate |
transform_back_or_terminate |
visit |
visit_value |
visit_front_value |
visit_back_value |
visit |
visit_or_terminate |
visit_front_or_terminate |
visit_back_or_terminate |
visit |
visit_and_crop |
visit_front_and_crop |
visit_back_and_crop |
visit |
visit_and_grow |
visit_front_and_grow |
visit_back_and_grow |
transform
系関数はインデックス値とそれによって引き当てた要素に対して何かする関数を受け取り、その関数の戻り値を(非参照で)返します。visit
系関数は新規追加する値と追加後の要素に対して何かする関数を受け取り、何も返しません。
これらの関数は多岐に渡りますが、実際にはコンテナ種別によってやることは変わらないため(最初に追加した2種類の関数をベースとするため)、実際にはメンバ関数ではなくフリー関数として追加することが望ましいです。それによって、std::vector
以外のコンテナでも同じように利用できるようになります。
提案には、このように追加される関数のstd::vector
に追加する場合の宣言例と実装例があります(長いのでコピペしません)。
この提案のメリットとしては
- 範囲アクセス関数のエラーを減らす
- 実行時のダングリング参照の発生を減らす
- 実行時の参照無効化エラーの発生を減らす
- プログラマが例外を扱う際に不必要な動的確保を減らす
などがあります。
P2956R0 Add saturating library support to std::simd
P0543で提案されている整数型の飽和演算関数群にstd::simd
のオーバーロードを追加する提案。
std::simd
はデータ並列型として、通常のスカラ型とほぼ同様に使用してハードウェアのSIMD演算を活用できるようにすることを意図したクラス型です。そのため、演算子オーバーロードや関数オーバーロードによって通常の四則演算や数学関数などを使用可能なようになっています。
P0543は整数型向けの飽和演算のためのライブラリ関数を提案するもので、C++26に採用される予定です(まだ承認前)。そこでは整数型のためのオーバーロードのみを提供しています。
この提案は、そこにstd::simd
向けのオーバーロードを追加しようとするものです。
提案されているオーバーロードでは、対象となる飽和演算は整数型を要素型とするstd::simd
の要素ごとに実行されます。
P2957R0 Contracts and coroutines
コルーチンに対して契約を有効化した場合に、各種の契約がどのように動作するのかについての提案。
コルーチンは、その関数定義内にコルーチン関連の3種類のキーワード(co_return, co_yield, co_await
)のいずれかが含まれている場合にその関数はコルーチンとなります。そのため、宣言だけからあるいは呼び出しだけからその関数がコルーチンであるかどうかを知ることはできません。
// コルーチンかもしれないし普通の関数かもしれない awaitable<int> session(int id); // 普通の関数 awaitable<int> default_session() { awaitable<int> s = session(0); // コルーチン呼び出しかもしれないし普通の関数呼び出しかもしれない return s; }
default_session()
は定義があるため普通の関数であることが分かりますが、session()
は宣言のみであるためどちらかは分かりません。
コルーチンを呼び出したとき、呼び出し側から見るとその呼び出しはコルーチンを起動しその戻り値オブジェクトを返すためのファクトリ関数(ramp関数と呼ぶらしい)を呼び出しいています。そして、このramp関数は契約を持つことができます(例えば上記例ではsession()
のid
が正であることなど)。
通常の関数とコルーチンの大きな違いはこの後、呼び出し側に戻った時に関数本体の実行が完了し終了しているかどうかです。通常の関数は確実に実行は終了しており、引数も含めた関数内部の状態は開放済みです。一方で、コルーチンではまだ終了しておらず単に中断しているだけの可能性があり、その場合関数内部状態はコルーチンステート内部に保存されています。そのため、コルーチンの観測可能な副作用はその最初の呼び出しがreturn
した後、任意のタイミングで変化し得ます。
したがって、ramp関数がreturn
した後その戻り値オブジェクトのいくつかの状態について何か期待を持っていたとしても、それは通常の関数に対するものよりも制限されるでしょう。
このように、コルーチンは通常の関数を一般化したもので、契約注釈が通常の関数と同様に動作するのかどうか(つまりその意味論について)はいくつかの疑問があります。この提案は、3種類の契約注釈がコルーチンにおいてどのように振舞うべきかを提案するものです。
とはいえ、アサーションに関してはあまり疑問は無いでしょう。アサーションが配置されるのはコルーチンの本体であり、それはコルーチンの実装詳細です。アサーションは現在のassert
マクロと同様に、実行がそこに到達したときに評価されることが期待されます。
残った2つ事前条件と事後条件はそれぞれ、関数が呼び出された時に期待すること、関数が正常にreturn
したときに関数が保証すること、を表しています。これらがチェックされるべき非コルーチン通常関数における適切な場所は次の場所です
- 事前条件 : 関数の呼び出し時、関数引数が初期化された直後
- 事後条件 : 関数が正常に
return
する時、戻り値が初期化され自動変数が破棄された後、関数引数が破棄される前
事前条件の実行時チェックによって関数の引数の予期しない組み合わせが関数本体で使用されないことが保証され、事後条件の実行時チェックによって呼び出し元が後続の処理を実行するにあたって望ましい状態のオブジェクト(またはプログラムの他の部分の状態)を確実に保証できるようになります。
awaitable<int> cancelable_session(int id) [[post r: is_cancelable(r)]]; template <typename T> void manage(awaitable<T> session) [[pre: is_cancelable(session)]]; void test() { awaitable<int> session = cancelable_session(1); // 契約を順守してmanage()を呼び出していることを確認するには // is_cancelable(session)がtrueであることを保証する必要がある manage(session); }
したがって、事前条件と事後条件は関数の呼び出し側と呼び出される側の関係性であり、呼び出し側は呼び出す関数のシグネチャのみを認識しその本体を認識しません。呼び出し側は呼び出しているものがコルーチンなのか関数なのかを知りません。
このようなことを前提とすると、コルーチンにおける事前条件のセマンティクスは、関数引数が初期化された直後、その引数が使用される可能性のあるコルーチンの初期アクションが実行される前、にチェックされることが最も適切です。
事後条件は呼び出されたコルーチンが呼び出し元に戻るタイミングが2種類あるために、より複雑になります。
通常の関数では、事後条件は戻り値に関して要求されることが多く、戻り値は関数の最後に作成されるため、事後条件が関数の最後に確立されると期待するのは自然です。そこでは、関数の終了時に何が起こるか?と、関数が呼び出し元にreturn
したときに何が起こるか?は同じ意味です。しかし、コルーチンの場合はその2つを区別する必要がある場合があり、その場合は後者のみが呼び出し側に関係します。
コルーチンとよく関連する非同期処理の観点から見ると、非同期処理A
を呼び出す呼び出し元は、呼び出したA
の実行結果には関心が無い場合がほとんどで、A
から戻り値の形で処理の成果を取得するわけではありません。
A
の呼び出し元はA
が成功したかどうか及びA
の処理成果を何らかのグローバル状態やコールバックなどの形でのみ受け取ります。そのため、A
の処理が成功しておりその成果が得られていれば、A
がどのように終了したか(A
の終了時に何が起こったか)は呼び出し側には関係がありません。この場合にもやはり、関数(A
)の終了時に何が起こるか?は呼び出し元には関係の無い性質です。
コルーチンによって非同期処理を行う場合、コルーチンの呼び出しにはタスクを非同期機構に投入するramp関数を呼び出す小さな初期の同期部分が存在します。呼び出し側は、この部分の成否には関心がある場合があります。つまり、ramp関数が処理を非同期機構に投入することに成功したかどうかを知りたい場合があります。
awaitable<int> cancelable_session(int id) [[post r: is_cancelable(r)]]; void caller() { awaitable<int> s = cancelable_session(1); [[assert: is_cancelable(s)]]; global_cancelable_sessions.push(std::move(s)); }
これは単純な例ですが、コルーチンがどのように終了したかよりも、コルーチンによって非同期処理が開始されたかどうか(ramp関数が成功したかどうか)に関心がある場合のものです。
ただ、コルーチンはその呼び出し時点の状態によっては、初期サスペンドポイントで中断せずに続行しそのまま終了することもできます。つまり、コルーチンは完全に同期的に(通常の関数と同じように)終了することができます。その内部状態や観測可能な副作用が何らかの方法で既知であるとは考えられないため、このことはコルーチンの事後条件がその直接の戻り値オブジェクトによって表される状態に限定されることを示唆しています。
そうはいっても、コルーチンの最後に何が起きたのかを知りたい場合もあるでしょう。しかし、コルーチンの終了はco_return
文の直接の到達によるものだけではなく(中断や例外送出など)、そのような終了に対する何らかの条件は呼び出し側と呼び出された側の合意を表すものではなく、別の種類の契約になります。
呼び出し側はコルーチン内部状態を推定できないため、呼び出し側がコルーチンの終了点の状態についてを実行時チェックで検証できることを保証できません。従って、コルーチンの終了点(中断点)における事後条件(コルーチン状態に関する仮想的な保証)はアサーションの方法でコルーチン内部でチェックされるべきです。
事後条件は呼び出し側に関連するものであり、呼び出し側は関数の宣言と呼び出し式そのものしか見えないため、呼び出しているのがコルーチンなのかどうかを知ることはできません。また、現在の契約プログラミング仕様では、関数の事後条件は呼び出し側でもその関数の終了直後に契約条件を評価することによって行える必要があります。
awaitable<int> cancelable_session(int id) [[post r: is_cancelable(r)]]; void caller() { awaitable<int> s = cancelable_session(1); // ここで事後条件を検証できるはず ... }
これらのことからこの提案では、コルーチンに対する事後条件を許可し、それはコルーチンのramp関数を呼び出した直後に評価され、ramp関数の呼び出しが終了した時点のプログラムの望ましい状態を記述するものとすることを提案しています。コルーチンに対する事後条件の評価は、コルーチン呼び出しの結果オブジェクトの初期化の直後に順序付けられ、コルーチンローカルの状態の破棄に対しては順序付けされません。事後条件から参照される関数引数は、コルーチンフレームにコピーされたものではなく、元の関数引数を参照します。
言い換えると、コルーチンに対する事前条件と事後条件の指定は、そのコルーチンの呼び出しが類似の契約条件指定を持つファクトリ転送関数にラップされた場合と同じように動作する必要があります。
awaitable<int> f1(int i) // coroutine [[pre: p(i)]] [[post r: q(r)]]; awaitable<int> f2(int i); // coroutine awaitable<int> ff2(int i) [[pre: p(i)]] [[post r: q(r)]]; { return f2(i); } void caller() { // これら2つの関数呼び出しは契約条件評価に関して類似したセマンティクスを持つ f1(1); ff2(1); }
P2958R0 typeof
and typeof_unqual
C23に導入されたtypeof
とtypeof_unqual
をC++でも有効化する提案。
C言語の次期バージョンであるC23には、長らく実装の拡張として存在してきたtypeof
演算子が導入され、そこから修飾情報を取り除いた型を得るtypeof_unqual
も同時に導入されました。
typeof
演算子は既にCで幅広く使用されており、C++とのコード共有部分においても使用されています。C++は標準としてはtypeof
演算子を持たず、typeof_unqual
は実装の拡張としても存在しなかったため、その存在がC23とのコード共有部分において問題となります。
typeof
演算子のやることはdecltype
とほぼ同じであるためマクロによるラップで単純には置換できますが、Cの式には値カテゴリの概念が無いことから結果が異なる場合がある他、typeof
/typeof_unqual
には式だけではなく型名も渡せるという微妙な違いがあります。
そのため、C23とのコード共有部分においては、C++のコードとしてtypeof
を完全には近似できません。
この提案は、C23との親和性向上のために、C++でもC23の意味論に沿ったtypeof
/typeof_unqual
演算子を追加しようとするものです。
P2960R0 Concurrency TS Editor's report for N4956
Concurrency TS v2の最新のワーキングドラフト(N4956)の変更点をまとめた文書。