文書の一覧
全部で29本あります。
- N4895 Working Draft, Extensions to C++ for Concurrency Version 2
- P1018R12 C++ Language Evolution status 🦠 pandemic edition 🦠 2021/06–2021/08
- P1072R9 basic_string::resize_and_overwrite
- P1169R2 static operator()
- P1206R5 Conversions from ranges to containers
- P1206R6 Conversions from ranges to containers
- P1664R5 reconstructible_range - a concept for putting ranges back together
- P1673R4 A free function linear algebra interface based on the BLAS
- P1885R6 Naming Text Encodings to Demystify Them
- P2047R2 An allocator-aware optional type
- P2093R8 Formatted output
- P2280R3 Using unknown references in constant expressions
- P2286R2 Formatting Ranges
- P2291R2 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header
- P2361R2 Unevaluated strings
- P2362R2 Remove non-encodable wide character literals and multicharacter wide character literals
- P2370R1 Stacktrace from exception
- P2372R2 Fixing locale handling in chrono formatters
- P2387R1 Pipe support for user-defined range adaptors
- P2388R1 Minimum Contract Support: either Ignore or Check_and_abort
- P2393R1 Cleaning up integer-class types
- P2414R1 Pointer lifetime-end zap proposed solutions
- P2415R1 What is a view?
- P2418R0 Add support for std::generator-like types to std::format
- P2419R0 Clarify handling of encodings in localized formatting of chrono types
- P2420R0 2021 Summer Library Evolution Polls
- P2423R0 C Floating Point Study Group Liaison Report
- P2425R0 Expression Function Body
- P2428R0 Slides: BSI issues with P2300
- おわり
N4895 Working Draft, Extensions to C++ for Concurrency Version 2
Concurrency TS v2のワーキングドラフト第一弾。
先頃アクセプトされた、ハザードポインタとRCUを反映したもので、今のところ他のものはありません。
これをベースとして実装経験を積んでから、標準ライブラリに導入される事になります。
P1018R12 C++ Language Evolution status 🦠 pandemic edition 🦠 2021/06–2021/08
EWG(コア言語への新機能追加についての作業部会)が2021/01–2021/03の間に議論した提案やIssueのリストや将来の計画、テレカンファレンスの状況などをまとめた文書。
8月は以下の提案がEWGでの投票にかけられる予定です。
- P2138R4 Rules of Design <=> Wording engagement
- P2266R1 Simpler implicit move
- P2128R5 Multidimensional subscript operator
- P2036R2 Changing scope for lambda trailing-return-type
- P2334R1 Add support for preprocessing directives elifdef and elifndef
- P2066R7 Suggested draft TS for C++ Extensions for Transaction Memory Light
- P2360R0 Extend init-statement to allow alias-declaration
- P2246R1 Character encoding of diagnostic text
- P2314R2 Character sets and encodings
- P2316R1 Consistent character literal encoding
これらの提案はほとんど、C++23入りを目指して提案をCWGに転送しようとするものです。
P1072R9 basic_string::resize_and_overwrite
std:string
に領域(文字長)を拡張しつつその部分を利用可能にする為のメンバ関数resize_and_overwrite()
を追加する提案。
以前の記事を参照
- P1072R6
basic_string::resize_and_overwrite
- [C++]WG21月次提案文書を眺める(2020年12月) - P1072R7
basic_string::resize_and_overwrite
- [C++]WG21月次提案文書を眺める(2021年02月) - P1072R8
basic_string::resize_and_overwrite
- [C++]WG21月次提案文書を眺める(2021年06月)
このリビジョンでの変更は、std::string
がstd::allocator_tratis
のconstruct/destroy
メンバ関数を使用しない事を明記したことなどの、提案する文言の調整です。
この提案はLWGのレビューを終え、次の全体会議で投票にかけられます。
P1169R2 static operator()
関数呼び出し演算子(operator()
)を、静的メンバ関数として定義できるようにする提案。
以前の記事を参照
このリビジョンでの変更は、EWGのレビューで提起されたラムダ式に関する2つの問題について追記したことです。
1つ目は、static
なラムダ式にキャプチャを許可するべきか?という問題です。ラムダ式本体でキャプチャを参照はしないが初期化キャプチャを利用したい、用途があるようです。例えば次のようなコードです
auto under_lock = [lock=std::unique_lock(mtx)]() static { /* do something */; };
現在(およびR1)の提案はstatic
ラムダはキャプチャを許可しないことになっています。それを緩和してこのような例を動作させることはできますが、それによってstatic
ラムダはステートレスラムダをstatic
にするだけ、というこの提案の簡易さ(教えやすさ)が失われます。
レビューのミーティング中に投票が行われましたが、そこではこれを許可するコンセンサスは得られませんでした。そのため、この提案ではこの点について変更はありません。
2つ目の問題は、キャプチャレス(ステートレス)ラムダ式はデフォルトでstatic
ラムダである、と実装定義にすることは可能か(あるいは望ましいか)?という点です。
この提案による変更は後方互換性がなくABIを破壊するため、現在(およびR1)の提案はstatic
はあくまでユーザーが指定するものです。実装定義のコンパイラオプションによってこの振る舞いがデフォルトになればユーザーの手間をかけずともこの提案による恩恵を受けることができるようになります。もしC++11時点でこの提案の内容が考慮され採用されていれば、ステートレスラムダ式はデフォルトでstatic
担っていたはずです。
一方、実装定義でフォルトの振る舞いを変更すると、ラムダ式の移植性を損ねます。現在の仕様では、operator()
の性質をはじめとする観察可能なプロパティは移植可能であるように規定されており、この提案によるstatic
性も同様であるため、この性質がポータブルではないというのは奇妙でありラムダの設計に反している、と筆者の方は主張しています。
今のところ、2つ目の問題についての投票は行われておらず、提案もそれを可能なようにしてはいません。
P1206R5 Conversions from ranges to containers
↓
P1206R6 Conversions from ranges to containers
任意のrangeをコンテナへ変換/実体化させるためのstd::ranges::to
の提案。
- P1206R2 ranges::to: A function to convert any range to a container - [C++]WG21月次提案文書を眺める(2020年10月)
- P1206R3 ranges::to: A function to convert any range to a container - [C++]WG21月次提案文書を眺める(2020年11月)
- P1206R4 Conversions from ranges to containers - [C++]WG21月次提案文書を眺める(2021年07月)
R5での変更は
push_back_range/push_front_range
関数の追加- 文言の微修正
- パフォーマンスやベンチマークについてのノートの追記
このリビジョンでの変更は、
push_back_range/push_front_range
関数をprepend_range/append_range
にリネームした
ことなどです。
prepend_range/append_range
は任意のrange
をpush_front/push_back
する関数で、一部の既存コンテナにメンバ関数として追加されます。
P1664R5 reconstructible_range
- a concept for putting ranges back together
view
によって別のrange
に変換されてしまった範囲を、元のrange
(と同じ型)に戻す操作、std::ranges::reconstruct
と関連するコンセプトの提案。
以前の記事を参照
- P1664R3 reconstructible_range - a concept for putting ranges back together - [C++]WG21月次提案文書を眺める(2021年04月)
- P1664R4 reconstructible_range - a concept for putting ranges back together - [C++]WG21月次提案文書を眺める(2021年06月)
このリビジョンでの変更は、提案する文言を全体にわたって書き換えたこと、設計選択の動機についての追記、などです。
P1673R4 A free function linear algebra interface based on the BLAS
標準ライブラリに、BLASをベースとした密行列のための線形代数ライブラリを追加する提案。
このリビジョンでの変更は多岐に渡りますが、LEWGのレビューを受けての文言(ライブラリの規定)の更新や修正がメインです。
P1885R6 Naming Text Encodings to Demystify Them
システムの文字エンコーディングを取得し、識別や出力が可能なライブラリを追加する提案。
以前の記事を参照
- P1885R3 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2020年9月)
- P1885R4 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2020年11月)
- P1885R5 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2021年02月)
このリビジョンでの変更は、UTF7IMA
エンコーディングをリストに追加したこと、RFC3808の参照をIANA IANA Charset MIBへの参照で置き換えたこと、text_encoding::id
列挙値の基底の型がstd::int_least32_t
である理由の説明の追記、などです。
P2047R2 An allocator-aware optional type
Allocator Awareなstd::optional
である、std::pmr::optional
を追加する提案。
このリビジョンでの変更は、pmr::optional
を<optional>
へ移動したこと(以前は<pmroptional>
)、pmr::optional
をよりアロケータについてジェネリック(polymorphic_allocator
以外に対応する)にすることについての意見を追記したこと、フリー関数のswap()
を追加したこと、などです。
アロケータジェネリック化に関しては、「C++標準ライブラリは狭いサブセットを決め打ちするのではなく、複数のセマンティクスを表現できるような基礎部品を提供するべき」のような意見が上がりましたが、コンセンサスが取られたものではないため今の所提案には反映されていません。
P2093R8 Formatted output
std::format
によるフォーマットを使用しながら出力できる新I/Oライブラリstd::print
の提案。
以前の記事を参照
- P2093R0 Formatted output - [C++]WG21月次提案文書を眺める(2020年6月)
- P2093R1 Formatted output - [C++]WG21月次提案文書を眺める(2020年7月)
- P2093R2 Formatted output - [C++]WG21月次提案文書を眺める(2020年10月)
- P2093R3 Formatted output - [C++]WG21月次提案文書を眺める(2021年1月)
- P2093R4 Formatted output - [C++]WG21月次提案文書を眺める(2021年2月)
- P2093R5 Formatted output - [C++]WG21月次提案文書を眺める(2021年3月)
- P2093R6 Formatted output - [C++]WG21月次提案文書を眺める(2021年4月)
このリビジョンでの変更は、LLVMのraw_ostream
(ここで提案されている文字化け防止メカニズムと似たことを実装している)への参照と言及を追記したことです。
P2280R3 Using unknown references in constant expressions
定数式での参照のコピーを許可する提案。
以前の記事を参照
- P2280R0 Using unknown references in constant expressions - [C++]WG21月次提案文書を眺める(2021年01月)
- P2280R1 Using unknown references in constant expressions - [C++]WG21月次提案文書を眺める(2021年02月)
- P2280R2 Using unknown references in constant expressions - [C++]WG21月次提案文書を眺める(2021年05月)
このリビジョンでの変更は、R2でポインタに対して拡張された内容を元に戻した(ポインタについてはこの提案で扱わないことにした)ことです。ただし、this
ポインタの定数式での利用についてはそのまま含まれています。
P2286R2 Formatting Ranges
任意の範囲を手軽に出力できる機能を追加する提案。
以前の記事を参照
- P2286R0 Formatting Ranges - [C++]WG21月次提案文書を眺める(2021年1月)
- P2286R1 Formatting Ranges - [C++]WG21月次提案文書を眺める(2021年2月)
このリビジョンでの変更は提案する文言の初稿を追加したことです。
P2291R2 Add Constexpr Modifiers to Functions to_chars
and from_chars
for Integral Types in Header
std::to_chars, std::from_chars
を整数変換に関してconstexpr
にする提案。
以前の記事を参照
- P2291R0 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header - [C++]WG21月次提案文書を眺める(2021年02月)
- P2291R1 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header - [C++]WG21月次提案文書を眺める(2021年05月)
このリビジョンでの変更は、提案する文言の修正(constexpr
追加し忘れやコメントの追加)です。
P2361R2 Unevaluated strings
コンパイル時にのみ使用され、実行時まで残らない文字列リテラルについての扱いを明確化する提案。
以前の記事を参照
- P2361R0 Unevaluated string literals - [C++]WG21月次提案文書を眺める(2021年04月)
- P2361R1 Unevaluated string literals - [C++]WG21月次提案文書を眺める(2021年06月)
このリビジョンでの変更は、unevaluated-string-literalという用語をunevaluated-stringに置換したこと、プリント不可能な文字を禁止しない事とunevaluated-stringが式ではない事についての説明の追記、提案する文言の修正、などです。
P2362R2 Remove non-encodable wide character literals and multicharacter wide character literals
エンコード可能ではない、あるいは複数文字を含むワイド文字リテラルを禁止する提案。
以前の記事を参照
- P2362R0 Make obfuscating wide character literals ill-formed - WG21月次提案文書を眺める(2021年04月)
- P2362R1 Make obfuscating wide character literals ill-formed - WG21月次提案文書を眺める(2021年07月)
このリビジョンでの変更は、ワイド文字列リテラルに関する一部の追加した文言を削除した事です。
P2370R1 Stacktrace from exception
現在投げられている例外オブジェクトに基づくスタックトレースを取得できるようにする提案。
以前の記事を参照
このリビジョンでの変更は、capture_stacktraces_at_throw()
の名前をset_capture_stacktraces_at_throw()
に変更し、その状態を取得できるget_capture_stacktraces_at_throw()
を追加した事、Windowsでの実装アイデアの追記、提案する文言の修正、などです。
P2372R2 Fixing locale handling in chrono formatters
<chrono>
のフォーマッタがロケール依存でありそれを制御できない問題を修正する提案。
以前の記事を参照
このリビジョンでの変更は、提案する文言の修正などです。
P2387R1 Pipe support for user-defined range adaptors
ユーザー定義のRangeアダプタに対して、パイプライン演算子(|
)サポートを簡単に行えるユーティリティを提供する提案。
以前の記事を参照
このリビジョンでの変更は、機能テストマクロを追加した事です。
P2388R1 Minimum Contract Support: either Ignore or Check_and_abort
契約が破られた時でも継続しないコントラクトサポートを追加する提案。
以前の記事を参照
このリビジョンでの変更は
- 提案する文言の追加
- 設計についての説明の追記 : 異なる翻訳単位で同じ関数に互換性のない契約がなされていたら
- 呼び出されない限り存在は許可されるが、呼び出されたら未定義動作
- 設計についての説明の追記 : 事後条件では、値or右辺値参照関数引数を参照する
- Issueの解決 : オブジェクトは契約指定の式内では
const
として扱われない - Issueの解決 : 実装はIgnoreモードでもコア定数式の契約を実行時にチェックしてもいい
- 事後条件指定の代替構文案の追記
- 事後条件で多くの変数を参照し名前を付けるために、
[[post(r, a, b) : cond]]
のような構文を将来的に利用できる
- 事後条件で多くの変数を参照し名前を付けるために、
- 設計詳細と理論的根拠のセクションの拡充
などです。
P2393R1 Cleaning up integer-class types
整数型とみなせる型を指すinteger-class
の定義を修正する提案。
以前の記事を参照
このリビジョンでの変更は、LWGのフィードバックを反映しLWG Issue 3575も修正するようにしたことです。
これによって、全てのinteger-class
型はthree_way_comparable<strong_ordering>
のモデルとなります。
この提案は次の全体会議で投票にかけられることが決まっています。
P2414R1 Pointer lifetime-end zap proposed solutions
Pointer lifetime-end zapと呼ばれる問題の解決策の提案。
以前の記事を参照
このリビジョンでの変更は
- Abstractの変更
- この提案の要約の追記
- “What We Are Asking For”セクションに、atomicとvolatileについての前方参照を追加
atomic_usable_ref()
を追加し、usable_ptr::ref
をusable_ref
にリネーム- B5セクションをより明快になるように書きなおし
などです。
追記された要約によれば、この提案の目指すものは次の2つです。
std::usable_ptr<T>
の標準ライブラリへの追加- これはポインタlikeな型で、参照先の生存期間が終了した後も使用可能であることが保証される
atomic
とvolatile
操作を再定義し、lifetime-end pointerの無効性を許容するようにする
また、この提案はbag-of-bitsポインタセマンティクス(ポインタは単なる値である、というようなポインタ意味論)を導入するものではないことが明確化されています。
P2415R1 What is a view?
view
コンセプトの要件を緩和する提案。
以前の記事を参照
このリビジョンでの変更は提案する文言を追加した事です。
SG9でのレビューでは全会一致でLEWGへの転送が決定されました。
P2418R0 Add support for std::generator-like types to std::format
std::generator
-likeな型に対する<format>
のサポートを追加する提案。
<format>
にrange
のフォーマットサポートを追加するP2286の作業中に、次のような問題が発見されました。
auto ints_coro(int n) -> std::generator<int> { for (int i = 0; i < n; ++i) { co_yield i; } } std::format("{}", ints_coro(10)); // error
std::format
は出力する値をconst T&
で受け取りますが、std::generator
はconst-iterableでもcopyableでもないためそこから値を取り出す事ができず、エラーとなります。
同様の問題が起こりうる<ranges>
の各種view
では、const-iterableではないview
はcopyableであるためC++20では問題になりませんでした。
しかし、std::generator
も含めて他のコルーチン型では同様の問題が発生し、またview
の中にもconst-iterableでもcopyableでもないものがある可能性があります。
この提案では、std::format
をはじめとする関数の出力対象引数列の型をconst Args&&...
からArgs&&...
に変更する事で問題の解決を図ります。これによって次のようなメリットが得られます
- const-iterableではない
view
はコピーを回避できる - 一般的なlifetimeエラーを検出できるようになる
2番目のメリットは次のような事が可能になる事です
// format_joinは、第一引数のrangeの各要素を第二引数のデリミタで区切って出力する関数(未導入) auto joined = std::format_join(std::vector{10, 20, 30, 40, 50, 60}, ":"); std::format("{:02x}", joined); // 現在はUB、この提案の後ではコンパイルエラー
そもそも<format>
が出力対象引数をconst
参照で受け取っていたのは、ビットフィールドをサポートするためでした
struct S { int bit: 1; }; auto s = S(); std::format("{}", s.bit); // 現在は有効、この提案の後ではコンパイルエラー std::format("{}", +s.bit); // intへ変換する、この提案の後でもOK
ビットフィールドはconst
参照でしか参照する事ができないため、この提案によってこのようなコードはエラーとなります。しかしその場合でも、ビットフィールドの頭に+
をつけて整数型にキャストする事で簡単に問題を回避でき、問題ないとの認識のようです。
この部分の6割は以下の型のご指摘によって出来ています
P2419R0 Clarify handling of encodings in localized formatting of chrono types
<chrono>
のフォーマットにおいて、実行時ロケールが指定するエンコーディングとリテラルエンコーディングが異なる場合の振る舞いを規定する提案。
std::locale::global(std::locale("Russian.1251")); auto s = std::format("День недели: {}", std::chrono::Monday); auto s = std::format("День недели: {:L}", std::chrono::Monday); // P2372以降 // 出力例(リテラルエンコーディングがUTF-8の場合) // "День недели: \xcf\xed"
このようなコードにおいて、リテラルエンコーディング(文字列リテラルのエンコーディング)がUTF-8の場合、グローバルロケールに指定されているRussian.1251
エンコーディングとの間に不一致があります。しかし、この場合の振る舞いを標準は指定していません。
この提案は、この場合に結果が一貫したエンコーディングの下で正しく出力されるように、実装にトランスコードィングを許可するか、ロケールを置換する事で文字化けを防ぐように仕様を明確化するものです。
このリビジョンでは、文字列リテラルのエンコーディングがユニコードでありロケールの指定するエンコーディングと異なる場合、ロケールによる文字列置換結果は、文字列リテラルのエンコーディングに変換されて出力される、ようにする事を提案しています。
std::locale::global(std::locale("Russian.1251")); auto s = std::format("День недели: {}", std::chrono::Monday); auto s = std::format("День недели: {:L}", std::chrono::Monday); // P2372以降 // 出力(リテラルエンコーディングがユニコードの場合) // "День недели: Пн"
P2420R0 2021 Summer Library Evolution Polls
2021年の夏(7月から9月にかけて)に予定されている、LEWGでの全体投票の予定表。
以下の9つの提案が投票にかけられる予定です。
- P2138R4 Rules of Design <=> Wording Engagement
- 機能の提案ではない
- P2372R1 Fixing Locale Handling In Chrono Formatters
- C++20への逆適用を推奨
- P1206R6
ranges::to
- P0533R8 constexpr For
<cmath>
And<cstdlib>
- P2273R2 Making
unique_ptr
constexpr
基本的にはLEWGでの作業を完了してLWG(CWG)へ転送することを確認するための投票です。
P2423R0 C Floating Point Study Group Liaison Report
C23に適用される予定の、浮動小数点数関連の変更についての要約文書。
- 2進浮動小数点数
- 幅を示す整数値を返すマクロの追加
- 浮動小数点環境アクセスのためのマクロと関数の追加
- その他マクロと関数の追加
fromfpx, roundeven, fmaxmag, llogb, nextup, fadd, ffma, totalorder, canonicalize, setpayload, strfromd
など
- Constant rounding modeの追加
#pragma STDC FENV_ROUND
ディレクティブによって設定し、いくつかの標準関数が影響を受ける
- signaling NaNのためのマクロ追加
- 浮動小数点数値分類のためのマクロの追加
iscanonical, issignaling, iszero
など
- 10進浮動小数点数(条件付きサポート)
float、double、long double
に対応する10進浮動小数点数型_Decimal{32,64,128}
- リテラルサフィックス
df/DF, dd/DD, dl/DL
- 10進浮動小数点数値に関するマクロ
- 最大・最小値の問い合わせや
DEC_EVAL_METHOD
など
- 最大・最小値の問い合わせや
- 10進浮動小数点数値と2進浮動小数点数に対応する浮動小数点環境モードに関するマクロと関数
- 10進浮動小数点数型に関する情報を取得する関数
samequantumd32, llquantexpd64
など
- 10進浮動小数点数型間の変換を行う関数
encodedecd128, decodebind64
printf/scanf
ファミリの10進浮動小数点数型サポート
- 交換・拡張浮動小数点数型(interchange and extended floating-point types)
- 2進と10進浮動小数点数型の交換と拡張のための個別の型
_Float32, _DecimalN, _FloatNx
- リテラルサフィックス
fN/FN, fNx/FNx, dN/DN, dNx/DNx
- 交換・拡張型に一般化された2進、10進浮動小数点数型情報取得マクロ
FLTN_MAX, DECNX_TRUE_MIN
- 交換・拡張型に一般化された2進、10進浮動小数点数型の関数やタイプジェネリックマクロやその他のマクロ
coshfN, ceilfNx, sinhdNx, dMadddNx, strtofN, FP_FAST_FMADDFN, FLTN_SNAN
- 交換・拡張型に一般化された10進浮動小数点数型用の関数
encodedecdN, quantizedNx
- 交換・拡張型に一般化された2進複素数型及び虚数型
_FloatN _Imaginary, _FloatNx _Complex
- 交換・拡張型に一般化された2進複素数型用の関数
cexpfN, crealfNx
- 評価メソットマクロの値を交換・拡張型を含めるように更新
_DecimalN
に対してDEC_EVAL_METHOD N
_FloatNx
に対してFLT_EVAL_METHOD N+1
- 算術演算が定義されない交換型の間の変換のためのデコード/エンコード関数
decodefN, dMecndecdN
- 2進と10進浮動小数点数型の交換と拡張のための個別の型
- 追加の数学関数
だいたい条件付きサポート(必須でない)だったりしますが、C23に向けてこれらの浮動小数点数関連の拡張が予定されています。おそらくC++にも影響してくるでしょう。
P2425R0 Expression Function Body
簡易な関数定義を式で直接書けるようにする提案。
ラムダ式を含む関数定義では、1行で済むような単純な関数を定義するシーンがよくあります。
// 1. Calling a member std::all_of(vs.begin(), vs.end(), [](const auto& val) { return val.check(); }); // 2. Binding an object std::all_of(vs.begin(), vs.end(), [id](const auto& val) { return val.id == id; }); // 3. Passing a lazy argument auto get_brush_or(painter, []{ return Brush(Color::red); }); // その他多数の例が提案にあります、省略
この例はほんの一例でしかなく、noexcept
やコンセプトチェックを欠いているなど正確なものではありません。このようなコードを正しく書くことは、記述量が増加するとともに考慮すべき事も多く、簡単な作業ではありません。
この提案の目的は、このような簡易な関数定義について記述量を削減するとともに簡易な記述で正しく書く事ができる構文を提供する事です。
コンセプトの導入によってSFINAEという高度なテクニックが非常に簡易な形で誰でも利用できるようになった事で、これらの問題の影響は時間経過とともに増大する事が想像されます。
void something(std::invocable<int> auto f); void something(std::invocable<std::string> auto f); // something()の呼び出しは曖昧であるため、コンパイルエラー something([](auto arg){ return arg/2; });
このように、コンセプトのチェックを必要とするコードは今後増加していく事でしょう。これはもはやTMP的なコードではなくあらゆる場所で行われるようになるため、何も考えずに書いても正しく動くことの重要性はより高まります。
さらに、静的例外(P0709R0)の導入は例外指定の問題をさらに悪化させます。
auto func(const std::string&) throws; // 静的例外の指定 auto func(int); ... std::transform(vs.begin(), vs.end(), vs.begin(), [](const auto& val) { return func(val); }); // このラムダ式の例外指定は?
現在の提案の仕様では、静的例外指定された関数をラムダ式で包んで特に例外指定を行わない場合、動的例外に変換されます。これは明らかに望ましい振る舞いではありません。
これらの問題は、短縮ラムダ提案(P0573R2)の解決しようとした問題でもありましたが、それは次のような理由によりリジェクトされました。
- 通常のラムダ式と意味論が異なる。関数本体が同じでも、短縮形か否かによって戻り値型が異なる。
- 任意の先読みパースが必要となる。パーサーはラムダ式が短縮形かどうかを判定するためにラムダ式本体を先読みしなければならない。
- 後置戻り値型との不一致。ラムダ本体と後置戻り値型とでは解析が異なるため、短縮ラムダは意図通りに動作しない可能性がある
- この問題はP2036R2で解決される(予定)
この提案は、この1つ目の問題を解決しようとするものでもあります。
短縮ラムダ提案では次の二つの形式が同じ意味となるように定義されました。
[]() noexcept(noexcept(expression)) -> decltype((expression)){ return expression; } []() => expression;
問題となったのは戻り値型のdecltype((expression))
です。これによる推論は参照のセマンティクスをデフォルトとし、左辺値のreturn
に対して左辺値参照型を推論します。一方、通常のラムダ式で戻り値型指定を省略した場合は値のセマンティクスがデフォルトであり、decltype((expression))
の結果型をdecay
した型が推論されます。
int i; auto l = [](int* p) noexcept(noexcept(*p)) -> decltype((*p)) { return *p; }; // decltype(l(&i))はint& auto l2 = [](int* p) { return *p; } auto func(int*) { return *p; } // decltype(l2(&i))とdecltype(func(&i))は共にint auto l3 = [](int* p) => *p; // decltype(l3(&i))はint&
また、[](auto val) => val;
のように書くとローカル変数への参照を返します。これはバグであり望ましい振る舞いではありませんが、先ほどのポインタの例のように多くの場合は参照を返した方が便利だと思われます。
このように、短縮ラムダは通常のラムダと同じようにはならず、これが敬遠された理由の一つとなりました。
この問題への対処のためにこの提案では2つの方法を提案しています。
- 最小式は参照セマンティクス持ち、非最小式は値のセマンティクスを持つ
- 最小式も値のセマンティクスを持ち、オプトインで参照セマンティクスを与える
この提案では1つ目の方を主として推しています。
提案1(メイン)
この提案による記法の1つ(非最小式)は、単一の式のみから構成された関数の{}
を取り払うことで導入されます。
// どこかで定義されているとして const pixel& pixel_ref_at(const image& image, point p) noexcept; // From auto pixel_at(image& image, int x, int y) { return pixel_at(image, point{x, y}); } // To (この提案) auto pixel_at(image& image, int x, int y) return pixel_at(image, point{x, y});
1つ目の方法ではこれは次のようなコードと等価となります。
auto pixel_at(image& image, int x, int y) noexcept(noexcept(std::decay_t<decltype(pixel_at(image, point{x, y}))>(pixel_at(image, point{x, y})))) -> decltype((std::decay_t<decltype(pixel_at(image, point{x, y}))>(pixel_at(image, point{x, y})))) { return pixel_at(image, point{x, y}); }
戻り値型の扱いは同じ(値のセマンティクス)ですが、例外指定の正確さとコンセプト/SFINAEとの高い親和性が追加されています。
従ってこの例では、提案前後でも戻り値型は変化しませんが、正しいnoexcept
ハンドリングが追加されます。
return
が必要であることは、呼び出されて値を返すという通常の関数のセマンティクスを持つことの指標となっています。
int i; auto l = [](auto* p) return *p; // decltype(l(&i))はint
記法の2つ目(最小式)は関数というよりは式である事を明示するもので、先ほどの記法からreturn
を取り除いたものです。
auto l = [](auto* p) *p; // このコードと等価 auto l = [](auto* p) noexcept(noexcept(*p)) -> decltype((*p)) { return *p; };
この記法では戻り値型は参照のセマンティクスを持ち、関数よりもより式そのものに近い振る舞いをします。そしてこれはP0573R2)の短縮ラムダのセマンティクスそのものです。
この提案による非最小式は、完全な関数とこの最小式の中間に位置する記法として振る舞い、この2つの記法がラムダ式以外の部分に広く導入されることによって、最小式による短縮ラムダと通常のラムダの間の曖昧さを取り除こうとするものです。
// このような階層的な記法を提供し、関数記法と最小式記法の間にギャップを挿入する [](auto* p) { return *p; } // 値セマンティクス [](auto* p) return *p; // 値セマンティクス [](auto* p) *p; // 参照セマンティクス
そして、最小式の記法によってより実際の式の表記に近づけることで、本体が値を返すという関数のメタファーから逃れる事を目指します。これは=>
を使用しない理由でもあります。=>
は結局return
のエイリアスであり、->
の進化形でしかありません。->
が型を返す事を示すのに対して=>
は式を返す事を示しています。何かを返すという関数的な概念をここでは避けようとしており、より純粋な式として振舞う事を明示的にしようとしています。
なお、この記法を導入すると関数の修飾との区別が曖昧になるため、それがある場合は修飾と式を:
で区切る事を提案しています。
auto Class::member() const: async; [](int a) mutable: coro;
提案1によるサンプルコード
現在 | この提案 |
---|---|
class QPointF { // ... real& rx() { return xp; } real& ry() { return yp; } real x() const { return xp; } real y() const { return yp; } friend auto operator+(const QPointF &p1, const QPointF &p2) { return QPointF(p1.xp+p2.xp, p1.yp+p2.yp); } private: real xp; real yp; }; |
class QPointF { // ... auto rx() xp; auto ry() yp; auto x() const return xp; auto y() const return yp; friend auto operator+(const QPointF &p1, const QPointF &p2) QPointF(p1.xp+p2.xp, p1.yp+p2.yp); private: real xp; real yp; }; |
現在 | この提案 |
---|---|
template< class C > constexpr auto cbegin( const C& c ) noexcept(noexcept(std::begin(c))) -> decltype(std::begin(c)) { return std::begin(c); } |
template< class C > constexpr auto cbegin( const C& c ) std::begin(c); |
提案2 (サブ)
こちらでは、提案1による最小式の戻り値型をデフォルトで値のセマンティクスとして、参照セマンティクスとするには追加の記法を用いるものです。
int i; auto l = [](int* p) *p; // decltype(l(&i))はint // このコードと等価 auto l = [](auto* p) noexcept(noexcept(std::decay_t<decltype(*p)>(*p))) -> decltype(std::decay_t<decltype(*p)>(*p)) { return *p; };
先ほどのような通常の関数定義から{}
とreturn
を省いた構文を導入する事は同じですが、ここではこれはまだ値のセマンティクスを持ちます。これを参照のセマンティクスとするには式を()
で囲みます。
int i; auto l = [](int* p) (*p); // decltype(l(&i))はint& // このコードと等価 auto l = [](auto* p) noexcept(noexcept(*p)) -> decltype((*p)) { return *p; };
変数を()
で囲んで参照を取得することは、decltype
やreturn
ですでに確立されています。
auto l = [](int i) -> decltype(auto) { return (i); }; // 戻り値型はint& struct Point { int x; int y; }; auto l2 = [](const Point& p) -> decltype(auto) { return (p.x); }; // 戻り値型はint& int i; decltype((i)) p = i; // pの型ははint&
このオプションの副次的な効果として、先ほど:
が必要だったところで不要となります。
[object]mutable: object.func(); // 値を返す最小式記法、区切りが必要 [object]mutable (object.func()); // 参照を返す最小式記法、区切りは不要
提案2によるサンプルコード
現在 | この提案 |
---|---|
class QPointF { // ... real& rx() { return xp; } real& ry() { return yp; } real x() const { return xp; } real y() const { return yp; } friend auto operator+(const QPointF &p1, const QPointF &p2) { return QPointF(p1.xp+p2.xp, p1.yp+p2.yp); } private: real xp; real yp; }; |
class QPointF { // ... auto rx() (xp); auto ry() (yp); auto x() const: xp; auto y() const: yp; friend auto operator+(const QPointF &p1, const QPointF &p2) QPointF(p1.xp+p2.xp, p1.yp+p2.yp); private: real xp; real yp; }; |
P2428R0 Slides: BSI issues with P2300
P2300の問題点や疑問点についての報告スライド。
欠いているアルゴリズムや、コールバックやキャンセルについてなど、いくつかの設計上と実装上の問題や疑問点についてまとめられています。