[C++]WG21月次提案文書を眺める(2021年08月)

文書の一覧

全部で29本あります。

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での投票にかけられる予定です。

これらの提案はほとんど、C++23入りを目指して提案をCWGに転送しようとするものです。

P1072R9 basic_string::resize_and_overwrite

std:stringに領域(文字長)を拡張しつつその部分を利用可能にする為のメンバ関数resize_and_overwrite()を追加する提案。

以前の記事を参照

このリビジョンでの変更は、std::stringstd::allocator_tratisconstruct/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の提案。

R5での変更は

  • push_back_range/push_front_range関数の追加
  • 文言の微修正
  • パフォーマンスやベンチマークについてのノートの追記

このリビジョンでの変更は、

  • push_back_range/push_front_range関数をprepend_range/append_rangeにリネームした

ことなどです。

prepend_range/append_rangeは任意のrangepush_front/push_backする関数で、一部の既存コンテナにメンバ関数として追加されます。

P1664R5 reconstructible_range - a concept for putting ranges back together

viewによって別のrangeに変換されてしまった範囲を、元のrange(と同じ型)に戻す操作、std::ranges::reconstructと関連するコンセプトの提案。

以前の記事を参照

このリビジョンでの変更は、提案する文言を全体にわたって書き換えたこと、設計選択の動機についての追記、などです。

P1673R4 A free function linear algebra interface based on the BLAS

標準ライブラリに、BLASをベースとした密行列のための線形代数ライブラリを追加する提案。

このリビジョンでの変更は多岐に渡りますが、LEWGのレビューを受けての文言(ライブラリの規定)の更新や修正がメインです。

P1885R6 Naming Text Encodings to Demystify Them

システムの文字エンコーディングを取得し、識別や出力が可能なライブラリを追加する提案。

以前の記事を参照

このリビジョンでの変更は、UTF7IMAエンコーディングをリストに追加したこと、RFC3808の参照をIANA IANA Charset MIBへの参照で置き換えたこと、text_encoding::id列挙値の基底の型がstd::int_least32_tである理由の説明の追記、などです。

P2047R2 An allocator-aware optional type

Allocator Awarestd::optionalである、std::pmr::optionalを追加する提案。

このリビジョンでの変更は、pmr::optional<optional>へ移動したこと(以前は<pmroptional>)、pmr::optionalをよりアロケータについてジェネリックpolymorphic_allocator以外に対応する)にすることについての意見を追記したこと、フリー関数のswap()を追加したこと、などです。

アロケータジェネリック化に関しては、「C++標準ライブラリは狭いサブセットを決め打ちするのではなく、複数のセマンティクスを表現できるような基礎部品を提供するべき」のような意見が上がりましたが、コンセンサスが取られたものではないため今の所提案には反映されていません。

P2093R8 Formatted output

std::formatによるフォーマットを使用しながら出力できる新I/Oライブラリstd::printの提案。

以前の記事を参照

このリビジョンでの変更は、LLVMraw_ostream(ここで提案されている文字化け防止メカニズムと似たことを実装している)への参照と言及を追記したことです。

P2280R3 Using unknown references in constant expressions

定数式での参照のコピーを許可する提案。

以前の記事を参照

このリビジョンでの変更は、R2でポインタに対して拡張された内容を元に戻した(ポインタについてはこの提案で扱わないことにした)ことです。ただし、thisポインタの定数式での利用についてはそのまま含まれています。

P2286R2 Formatting Ranges

任意の範囲を手軽に出力できる機能を追加する提案。

以前の記事を参照

このリビジョンでの変更は提案する文言の初稿を追加したことです。

P2291R2 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header

std::to_chars, std::from_charsを整数変換に関してconstexprにする提案。

以前の記事を参照

このリビジョンでの変更は、提案する文言の修正(constexpr追加し忘れやコメントの追加)です。

P2361R2 Unevaluated strings

コンパイル時にのみ使用され、実行時まで残らない文字列リテラルについての扱いを明確化する提案。

以前の記事を参照

このリビジョンでの変更は、unevaluated-string-literalという用語をunevaluated-stringに置換したこと、プリント不可能な文字を禁止しない事とunevaluated-stringが式ではない事についての説明の追記、提案する文言の修正、などです。

P2362R2 Remove non-encodable wide character literals and multicharacter wide character literals

エンコード可能ではない、あるいは複数文字を含むワイド文字リテラルを禁止する提案。

以前の記事を参照

このリビジョンでの変更は、ワイド文字列リテラルに関する一部の追加した文言を削除した事です。

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::refusable_refにリネーム
  • B5セクションをより明快になるように書きなおし

などです。

追記された要約によれば、この提案の目指すものは次の2つです。

  1. std::usable_ptr<T>の標準ライブラリへの追加
    • これはポインタlikeな型で、参照先の生存期間が終了した後も使用可能であることが保証される
  2. atomicvolatile操作を再定義し、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&&...に変更する事で問題の解決を図ります。これによって次のようなメリットが得られます

  1. const-iterableではないviewはコピーを回避できる
  2. 一般的な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つの提案が投票にかけられる予定です。

基本的にはLEWGでの作業を完了してLWG(CWG)へ転送することを確認するための投票です。

P2423R0 C Floating Point Study Group Liaison Report

C23に適用される予定の、浮動小数点数関連の変更についての要約文書。

  1. 2進浮動小数点数
    • 幅を示す整数値を返すマクロの追加
    • 浮動小数点環境アクセスのためのマクロと関数の追加
    • その他マクロと関数の追加
      • fromfpx, roundeven, fmaxmag, llogb, nextup, fadd, ffma, totalorder, canonicalize, setpayload, strfromdなど
    • Constant rounding modeの追加
      • #pragma STDC FENV_ROUNDディレクティブによって設定し、いくつかの標準関数が影響を受ける
    • signaling NaNのためのマクロ追加
    • 浮動小数点数値分類のためのマクロの追加
      • iscanonical, issignaling, iszeroなど
  2. 10進浮動小数点数(条件付きサポート)
  3. 交換・拡張浮動小数点数型(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
  4. 追加の数学関数

だいたい条件付きサポート(必須でない)だったりしますが、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)の解決しようとした問題でもありましたが、それは次のような理由によりリジェクトされました。

  1. 通常のラムダ式と意味論が異なる。関数本体が同じでも、短縮形か否かによって戻り値型が異なる。
  2. 任意の先読みパースが必要となる。パーサーはラムダ式が短縮形かどうかを判定するためにラムダ式本体を先読みしなければならない。
  3. 後置戻り値型との不一致。ラムダ本体と後置戻り値型とでは解析が異なるため、短縮ラムダは意図通りに動作しない可能性がある
    • この問題は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. 最小式は参照セマンティクス持ち、非最小式は値のセマンティクスを持つ
  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; }; 

変数を()で囲んで参照を取得することは、decltypereturnですでに確立されています。

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の問題点や疑問点についての報告スライド。

欠いているアルゴリズムや、コールバックやキャンセルについてなど、いくつかの設計上と実装上の問題や疑問点についてまとめられています。

おわり