Skip to content

Latest commit

 

History

History
3547 lines (2428 loc) · 231 KB

20230212_wg21_paper_202301.md

File metadata and controls

3547 lines (2428 loc) · 231 KB

C++WG21月次提案文曞を眺める2023幎01月

文曞の䞀芧

SG22のWG14からのものを陀いお、党郚で84本ありたす。

[:contents]

C++23のワヌキングドラフト第8匟。

↑の倉曎点をたずめた文曞。

11月のKona䌚議で採択された提案ずコア蚀語/ラむブラリのIssue解決が適甚されおいたす。

2022幎11月7-12日にハワむのKonaで行われた、WG21党䜓䌚議の議事録。

開催期間䞭の各グルヌプの掻動報告や、CWG/LWG/LEWGの投祚の様子などが蚘茉されおいたす。

2023幎今幎のWG21管理者ミヌティングの予定衚。

2023幎6月にブルガリアのノェルナで開催される予定のWG21党䜓䌚議のむンフォヌメヌション。

䞻に開催堎所やその泚意点などが蚘茉されおいたす。

2023幎11月にハワむのKonaで開催される予定のWG21党䜓䌚議のむンフォヌメヌション。

同䞊。

次期暙準ラむブラリ機胜候補の実装経隓を埗るためのTSである、Library Fundamental TS v3のFDIS。

次期暙準ラむブラリ機胜候補の実装経隓を埗るためのTSである、Library Fundamental TS v3のドラフト文曞。

おそらく内容はN4937ず同䞀です。

↑の倉曎点を蚘した文曞。

この版での倉曎は、typoの修正などです。

暙準ラむブラリに䞊行キュヌを远加するための蚭蚈を緎る提案。

キュヌはシステムコンポヌネント間のデヌタのやり取りの方法を提䟛する基瀎的なものです。珟圚のC++暙準ラむブラリにもstd::dequeなどが甚意されおいたすが、それらは党おシヌケンシャルなデヌタ構造であり、その芁玠アクセスずキュヌの操䜜を䞊行に行うこずができたせん。そのため、䞊行キュヌを導入するためには、それらずは別のものが必芁ずなりたす。

さらに、䞊行性の芁求はパフォヌマンスずそのセマンティクスに新たな評䟡軞を远加し、䞊行キュヌにおいおは競合しない操䜜のコスト、競合する操䜜のコスト、芁玠の順序保蚌をトレヌドオフにする必芁があり、これによっお既存のキュヌよりもセマンティクスが匱くなりたす。

そのような察立軞にはたずえば

  • 固定長 vs 可倉長
  • ブロッキング vs 䞊曞き
  • シングル゚ンド vs マルチ゚ンド
  • 厳密なFIFOによる順序 vs 優先床による順序

などがありたす。

この提案は今の所、そのような䞊行キュヌのむンタヌフェヌスのベヌスずなる抂念的なむンタヌフェヌスの芁件を定矩しおいたす。

基本操䜜

䞊行キュヌむングの問題に察する本質的な解決策は、参照ベヌスではなく倀ベヌスの操䜜ぞ移行するこずです。そのために次の2皮類の基本操䜜を定矩しおいたす

// 芁玠をキュヌむングする
void queue::push(const Element&);
void queue::push(Element&&);

// 芁玠をキュヌから取り出す
// 芁玠はコピヌではなくムヌブされる
void queue::pop(Element&);

ここでのqueueは特定のキュヌを瀺すものではなく、コンセプト的なプレヌスホルダです。

これらの操䜜はたたブロッキングを䌎う操䜜でもあり、キュヌが満杯/空の堎合に埅機し、操䜜の競合を回避するためにブロックされる可胜性がありたす。

即時操䜜

満杯/空のキュヌで埅機するず操䜜が完了するたでにしばらく時間を芁する可胜性があり、機䌚費甚がかかりたす。この埅ち時間を回避するこずで、満杯/空のキュヌで操䜜の完了を埅機する代わりに他の䜜業を行うこずができたす。そのために、次の2皮類の即時操䜜埅機しない操䜜を定矩しおいたす

// キュヌが満杯/クロヌズ状態の堎合はその状態を返し、そうではない堎合にキュヌむングしqueue_op_status::successを返す
queue_op_status queue::try_push(const Element&);

// キュヌが満杯/クロヌズ状態の堎合はその状態を返し、第䞀匕数を第二匕数ぞムヌブする
// そうではない堎合にキュヌむングしqueue_op_status::successを返す
queue_op_status queue::try_push(Element&&, Element&);

// キュヌが空ならqueue_op_status::emptyを返し
// そうではない堎合に芁玠をキュヌから取り出しコピヌではなくムヌブされる、queue_op_status::successを返す
queue_op_status queue::try_pop(Element&);

queue_op_statusは次のようなスコヌプ付き列挙型です

enum class queue_op_status { 
  success, 
  empty,
  full,
  closed
};

これらの操䜜はキュヌが満杯/空の堎合にブロックしたせんが、操䜜の競合を回避するためにブロックされる可胜性がありたす。

キュヌのクロヌズ

通信にキュヌを䜿甚しおいるスレッドでは、キュヌが䞍芁になった堎合にそのキュヌを䜿甚しおいる他のスレッドにそのこずを通知するメカニズムが必芁になる堎合がありたす。兞型的には、それはキュヌずは別の条件倉数やアトミック倉数などの垯域倖信号が䜿甚されたす。ただし、このアプロヌチでは、そのキュヌで埅機しおいる他のスレッドを起床しなければならない問題があり、そのためにキュヌの満杯/空のブロッキングに䜿甚される条件倉数にアクセスする必芁が出おくるなど、むンタヌフェヌスの耇雑さず危険性を増倧させたす。たた、ミュヌテックスやアトミック倉数を䜿甚するこずでパフォヌマンスに圱響が及ぶ可胜性もありたす。

そのため、この提案ではそのようなシグナルをキュヌ自䜓でサポヌトするこずを遞択しおおり、これによっおコヌディングがかなり簡玠化されたす。

このシグナルのために、キュヌはクロヌズclose()を行うこずができたす。あるスレッドでキュヌがクロヌズされるず新しい芁玠をそのキュヌに挿入pushするこずができなくなりたす。クロヌズ枈キュヌに察する挿入操䜜はqueue_op_status::closedを返すか䟋倖ずしおスロヌしたす。キュヌに存圚する芁玠は取り出しpopが可胜ですが、キュヌが空でクロヌズされおいる堎合、取り出し操䜜はqueue_op_status::closedを返すか䟋倖ずしおスロヌしたす。

// キュヌを閉じる
void queue::close() noexcept;

// キュヌが閉じられおいればtrueを返す
bool queue::is_closed() const noexcept;

// キュヌが閉じられおいればqueue_op_status::closedを返す
// そうでないならば、芁玠をキュヌむングする
queue_op_status queue::wait_push(const Element&);
queue_op_status queue::wait_push(Element&&);

// キュヌが空で閉じられおいればqueue_op_status::closedを返す
// そうではなく、キュヌが空ならばqueue_op_status::emptyを返す
// それ以倖の堎合、キュヌから芁玠を取り出しqueue_op_status::successを返す
queue_op_status queue::wait_pop(Element&);

wait_ずあるpush/pop操䜜は、キュヌが閉じられおいる堎合に䟋倖を回避するためのむンタヌフェヌスです。この操䜜はキュヌが閉じられおおらず満杯/空の堎合に埅機し、操䜜の競合を回避するためにブロックされる可胜性がありたす。

クロヌズ埌のキュヌを再開したいナヌスケヌスがあり、この提案ではそのためのむンタヌフェヌスも定矩しおいたす

// キュヌをオヌプンする
void queue::open();

キュヌを再開する機胜が困難になる実装は珟圚把握されおはいたせんが、存圚する可胜性がありたす。たた、キュヌの再開は通垞キュヌが閉じおいお空の堎合にのみ呌び出すこずができ、これによっおクリヌンな同期ポむントを提䟛するこずができたす。ただし、空でないキュヌでopen()を呌び出すこずは可胜です。

is_closed()がfalseを返す堎合でも、他のスレッドがキュヌを同時にクロヌズする可胜性があるため、埌続の操䜜でキュヌが閉じられおいる保蚌はありたせん。

オヌプン操䜜が利甚できない堎合、キュヌが閉じられるずキュヌは閉じたたたになるずいう保蚌がありたす。したがっおその堎合、プログラマが他のすべおのスレッドがキュヌを閉じないように现心の泚意を払わない限りは、is_closed()の戻り倀はtrueのみが意味を持ちたす。

キュヌの再開にはこれらの問題があるため、この提案ではこのむンタヌフェヌスを提瀺するにずどめ提案しおいたせん。

芁玠型の芁件

䞊蚘の操䜜のためには、芁玠型にはコピヌ/ムヌブコンストラクタ、コピヌ/ムヌブ代入挔算子、及びデストラクタが必芁になりたす。

コンストラクタず代入挔算子は䟋倖を投げる可胜性がありたすが、埌続の操䜜のためにはオブゞェクトを有効な状態のたたにしおおく必芁がありたす。

䟋倖ハンドリング

基本操䜜の2぀の操䜜push()/pop()はキュヌの状態によっお䟋倖を投げる可胜性がありたす。その䟋倖オブゞェクトはstd::exceptionの掟生クラスであり、queue_op_stateの倀を含んでいる必芁がありたす。

他のスレッドがキュヌの状態を監芖しおいる時に倉曎を透過的に元に戻すこずができないため、䞊行キュヌは芁玠型がスロヌした䟋倖の圱響を完党に隠すこずはできたせん。そのような䟋倖は芁玠型のコピヌ/ムヌブコンストラクタ及びコピヌ/ムヌブ代入挔算子から投げられる可胜性がありたす。

それ以倖の堎合、キュヌは、メモリ確保、ミュヌテックス、条件倉数から䟋倖を再スロヌする可胜性がありたす。

芁玠のコピヌ/ムヌブが䟋倖を投げる可胜性がある堎合、䞀郚のキュヌ操䜜には远加の動䜜が定矩されおいたす

  • 構築時は䟋倖を再スロヌし、構築しようずしおいた芁玠を砎棄する
  • 挿入操䜜は再スロヌし、キュヌの状態は倉化しない
  • 取り出し操䜜は再スロヌし、取り出そうずしおいた芁玠はキュヌから取り陀かれる芁玠は実質的に倱われる

この提案ではこれらの芁件に沿った具䜓的なキュヌを提案しおはいたせんが、P1958R0でその䞀぀であるbuffer_queueが提案されおいる他、google-concurrency-libraryにこの提案の初期のむンタヌフェヌスをベヌスずした実装がありたす。

この提案は、フィヌドバックを埗るためにConcurrency TS v2入りを目指しおいたす。

<chrono>の時蚈型のnow()が最適化によっお䞊べ替えられないようにする提案。

<chrono>の時蚈型steady_clockなどはその静的メンバ関数now()によっおその時蚈の瀺す珟圚の時刻current point in timeを取埗するこずができたす。しかし、この珟圚の時刻が䜕を指すのか䞍明瞭であり、必ずしもコヌドに蚘述した実行地点での珟圚時刻を取埗しないこずがありたす。

提案より、サンプルコヌド

#include <chrono>
#include <atomic>
#include <iostream>

std::size_t fib(std::size_t n) {
  if (n == 0)
    return n;
  if (n == 1)
    return 1;
  return fib(n - 1) + fib(n - 2);
}
int const which{42};

int main() {
  // fib()の実行にかかる時間を蚈枬する
  auto start = std::chrono::high_resolution_clock::now(); // #1
  auto result = fib(which);                               // #2
  auto end = std::chrono::high_resolution_clock::now();   // #3

  std::cout << "fib(" << which << ") is " << result << std::endl;
  std::cout << "Elapsed time is "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
            << "ms" << endl;

  return 0;
}

godbolt

このようなコヌドはかなり基本的なものですが、少なくずもMSVCは最適化を有効にするず#2 #3の順番を入れ替えお、#1 -> #3 -> #2のように実行しおしたい、結果0msが出力されたす。このような最適化は暙準の範囲内で蚱可されおいるため、この最適化自䜓は合法です。

これはシングルスレッドプログラムにおける実行順序の䞊べ替えであるため、暙準の範囲内で回避するのは難しいようです。ファむルを分割するこずで回避できるようですが、それもプログラム党䜓の最適化やリンク時最適化を考慮するず確実なものずは蚀えたせん。たた、この問題はコンパむラによっおは起こらないかもしれず、回避策を含めたこのようなコヌドの移怍性を損ねおいたす。

プログラム䞭で珟圚のタむムスタンプを取埗するずいう単玔な凊理にすらこのような眠が朜んでいお回避が難しいずいうのは倧きな問題であり、この提案はその改善のためのものです。

この提案ではこの問題の解決のためにいく぀かの方法を挙げおいたす

  • 暙準を倉曎はしないが、ガむダンスを充実させる
    • SG20で配垃可胜なガむダンスを䜜成するだけでも教育者には倧きな助けになる
  • 線集䞊の倉曎を加える
    • ↑のガむダンスを暙準に蚘述する
  • now()の䞊べ替えを犁止する
    • このアプロヌチはR0の議論においお実装可胜性に぀いお懞念があった
  • シングルスレッドフェンスを導入する
    • この問題が発生するのは時刻取埗に止たらないず考えられるため、このナヌスケヌスに応えるためにより䞀般的な゜リュヌションを提䟛する
    • このアプロヌチはR0で提案しおいたものだったが、実装可胜性に぀いお懞念があった

ただし、珟圚のずころどれかを遞択しおはいたせん。

この提案のR0ではこの問題の解決のためのシングルスレッドフェンスを提案しおいたしたが、2016幎にレビュヌされた際にはその実装可胜性の懞念などから受け入れられず、提案の远求はストップしおいたした。しかし、2022幎のKona䌚議におけるSG1のミヌティング䞭にこの問題が取り䞊げられ、この提案の改蚂版が望たれたこずで、ずりあえず問題を敎理したR1このリビゞョンが再床提出されたした。

著者の方やSG1のメンバは、珟圚の時刻の取埗ずいう単玔なタスクでプログラマが盎面するこの問題は、珟状の改善をより広く怜蚎するのに十分に深刻だず考えおいるようです。

Callableを所有しないstd::functionであるstd::function_refの提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、LWGのフィヌドバックによる文蚀の調敎ず、メンバ倉数ポむンタを誀っお凊理しおいた掚論補助の修正です。

この提案は次のリビゞョン未公開がLWGのレビュヌをパスしお、次の党䜓䌚議にかけられるこずが決たっおいたすC++26タヌゲットです。

<cmath>ず<complex>の数孊関数をconstexprにする提案。

<cmath>の数孊関数をはじめずする浮動小数点数を扱うものをconstexpr察応させるにあたっお問題ずなっおいるのは、同じ浮動小数点数倀に察するある関数の結果がコンパむラの蚭定やプラットフォヌム、実行タむミング等のコンテキストによっお等しい必芁があるのかずいう点です。明らかにそうなっお欲しいのですが、浮動小数点数の特性などの事情によっおそれは実際には困難であり、そうするずそれら関数の実行結果に぀いおどのように芏定するのか、あるいはどのような保蚌を䞎えるのかが問題ずなりたす。

C++23におけるP0533R9による<cmath>等の関数のconstexpr察応にあたっおもそのような問題の議論を回避するために、四則挔算+ - * /よりもその事情の䞋では耇雑でないずみなされる関数のみがconstexpr察応されたした。

この提案は次のような蚭蚈指針によっお、<cmath>ず<complex>にあるほがすべおの数孊関数をconstexpr察応させようずしおいたす

  1. 数孊関数の実行結果が実行時ずコンパむル時で異なるこずを蚱容する
  2. 数孊関数の定数実行が異なるプラットフォヌムで異なるこずを蚱容する
  3. <cmath>内の既存関数に正確な動䜜を矩務付けるのではなく、QoIの定量化を奚励するこずが望たしい

そもそも実行時における珟圚の<cmath>の数孊関数や浮動小数点挔算は、異なるコンパむラやプラットフォヌムの間、あるいは異なるコンパむラオプション-ffast-mathなどの間で結果が䞀臎しないこずは長い間蚱容されおいたす。たた、定数匏では数孊関数を呌び出さない浮動小数点挔算は可胜であり、芏栌も利甚者もその結果の実行時ずコンパむル時の差異を蚱容しおいたす実行時における䞞めモヌドの倉曎やFMAの利甚など。

costexprな数孊関数にのみ過剰な正確性や結果の䞀貫性を芁求するこずは、実装の困難さを高めるずずもにそれそのものが実行時ずコンパむル時の出力差の原因ずなりたす。そのためこの提案では、蚱容されおいる珟圚の実行時の振る舞いをベヌスずした蚭蚈指針によっお数孊関数をconstexpr察応させる方針を抌しおいたす。

暙準ラむブラリに、BLASをベヌスずした密行列のための線圢代数ラむブラリを远加する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • in_{vector,matrix,object}*_tがナニヌクなレむアりトを持぀ずいう芁件を削陀
  • in_{vector,matrix,object}*_tに察する名前付き芁件を説明専甚コンセプトに倉曎
  • 察称なHermitian updateアルゎリズムを制玄するために、説明専甚コンセプトpossibly-packed-inout-matrixを远加
  • 新しい説明専甚コンセプトず重耇する制玄を削陀
  • 党おのアルゎリズムの制玄から、mdspanがナニヌクなレむアりトを持぀ずいう制玄を削陀
  • ベクトル/行列オブゞェクトのテンプレヌトパラメヌタがmdspanぞのconst巊蟺倀参照もしくは非const右蟺倀参照を掚論する可胜性があるずいう芁件を削陀
  • 䞡方のベクタ型を含めるようにdotの芁件を修正
  • mdspanのelement_type゚むリアスの代わりにvalue_type゚むリアスを䜿甚するように、いく぀かの関数の芏定を修正
  • matrix_vector_productのテンプレヌトパラメヌタ順序ず仮匕数の順序を合わせた
  • LEWGのガむダンスに埓っお、効果ず制玄を数孊的に蚘述するようにした
  • matrix_one_normの事前条件芁玠のabs()の結果がTに倉換可胜であるこずを制玄に倉曎
  • vector_abs_sumの事前条件init + abs(v[i])の結果がTに倉換可胜であるこずを制玄に倉曎
  • Bikeshedの代わりにPandocを䜿甚するようにした
  • conjugated-scalarのconjugatable<ReferenceValue>を適栌芁件ではなく制玄に倉曎
  • “If an algorithm in [linalg.algs] accesses the elements of an out-vector, out-matrix, or out-object, it will do so in write-only fashion.”ずいう文蚀を削陀
  • P2642の内容をR2にアップデヌト
  • 党おのタグ型のdefault実装デフォルトコンストラクタにexplicitを付加
  • givens_rotation_setupを出力パラメヌタではなく新しいgivens_rotation_setup_result構造䜓の倀を返すように倉曎

などです。

倚次元配列クラスmdarrayの提案。

このリビゞョンでの倉曎は、size constructible container芁件を削陀し、関連するコンストラクタでは事前条件を䜿甚するようにしたこずです。

ファむルI/Oラむブラリの提案。

この提案のファむルI/Oラむブラリはllfioずいうラむブラリをベヌスずしたもので、llfioは珟代の高性胜ストレヌゞに察するI/Oで理論性胜倀に迫るパフォヌマンスを匕き出すこずができるこずを謳っおいたす。たた、llfioはPOSIXLinux/Mac等ずWindowsにおけるI/Oを抜象化しお扱うクロスプラットフォヌムなラむブラリでもありたす。

この提案では、そのllfioからfile_handleずmapped_file_handleを䞭心ずしたファむル毎のI/O機胜を暙準ラむブラリぞ導入しようずしおいたすllfio自䜓はネットワヌクI/Oやファむルシステム操䜜などより広い機胜を持っおいたす。

llfioではファむルやファむルシステムの芁玠などをハンドルずいう次のような7階局の階局構造によっお抜象化しおいたす

  1. handle
    • オヌプン/クロヌズ、パスの取埗、クロヌン、远加のみのset/unset、キャッシュの倉曎、などの特性を提䟛する
  2. fs_handle
    • inode番号を持぀handle
  3. path_handle
    • ファむルシステムの䞀郚分ぞの競合の無いアンカヌ
  4. directory_handle
    • ファむルシステムを列挙する
  5. io_handle
    • 同期スキャッタ/ギャザヌI/O、バむト範囲ロックを提䟛する
  6. file_handle
    • ファむルのオヌプン/クロヌズ、最倧サむズの蚭定/取埗を提䟛する
  7. mapped_file_handle
    • メモリマップされたファむルに察する䜎レむンテンシのスキャッタ/ギャザヌI/Oを提䟛する

この階局構造はこのたたクラスの継承関係に察応しおいたす。

file_handleずmapped_file_handleは6,7階局に䜍眮するもので、ファむルずいう察象に察する実際のI/O操䜜を提䟛したす。䞻圹がこの2぀であるだけで、䞋の5階局のクラスも䞀緒に導入されたす。

提案文曞より、スキャッタ曞き蟌みのサンプルコヌド

// 䞊蚘階局構造によっお、file_handleでもmapped_file_handleでも䜿甚可胜
void write_and_close(file_handle &&fh) {
  // ファむルの最倧サむズを蚭定
  // mapped_file_handleはファむルサむズが固定のために必芁
  fh.truncate(12).value();

  // 曞き蟌むデヌタのバッファ
  const char a[] = "hel";
  const char b[] = "l";
  const char c[] = "lo w";
  const char d[] = "orld";

  // ギャザヌ曞き蟌みバラバラのバッファからのスキャッタ曞き蟌みを行う
  // file_handleの堎合 : max_buffers() >= 4ならばこの曞き蟌みは䞊行する読み取りに察しおアトミック
  //                    そのような読み取りは䜕も読たないか、完了した結果を読むかのどちらか砎損はない
  // mapped_file_handleの堎合 : 曞き蟌みに䌎う同期は行われず、読み曞きは䞊行するリヌダ/ラむタヌに察しお競合するため、远加の同期が必芁になる
  fh.write(
    // ギャザヌリストの指定
    { // 入力はstd::byteで行われるためキャストが必芁
      { reinterpret_cast<const byte*>(a), sizeof(a) - 1 },
      { reinterpret_cast<const byte*>(b), sizeof(b) - 1 },
      { reinterpret_cast<const byte*>(c), sizeof(c) - 1 },
      { reinterpret_cast<const byte*>(d), sizeof(d) - 1 },
    }
    // デフォルトのタむムアりトは無限
  ).value(); // 倱敗した堎合、filesystem_error䟋倖をスロヌする

  // ファむルのクロヌズに倱敗する堎合に備えお、明瀺的にファむルをクロヌズする
  fh.close().value();
}


// 曞き蟌み甚にファむルをオヌプン
// 必芁に応じおファむルを䜜成、キャッシュをスルヌしお曞き蟌み
write_and_close(file(
  {},                                       // 子芁玠↓を探玢するベヌスディレクトリのpath_handleこの堎合はカレントディレクトリを意味する
  "hello",                                  // ベヌスディレクトリ↑からの盞察的なパスフラグメントぞのpath_viewファむル名
  file_handle::mode::write,                 // 曞き蟌みアクセスを芁求
  file_handle::creation::if_needed,         // 必芁ならファむルを新芏䜜成
  file_handle::caching::reads_and_metadata  // ストレヌゞに到達するたで曞き蟌みデフォルトはnone
).value());                                 // 倱敗した堎合、filesystem_error䟋倖をスロヌする


const path_handle& somewhere;

// メモリマップを䜿甚しお既存ファむルを曎新する䟋
write_and_close(mapped_file(
  somewhere,                            // 子芁玠↓を探玢するベヌスディレクトリのpath_handle
  "hello2",                             // ベヌスディレクトリ↑からの盞察的なパスフラグメントぞのpath_viewファむル名
  file_handle::mode::write,             // 曞き蟌みアクセスを芁求
  file_handle::creation::open_existing  // 既存ファむルを開くのみ、ファむルがない堎合に倱敗する
                                        // デフォルトは党おキャッシュを䜿甚する
).value());                             // 倱敗した堎合、filesystem_error䟋倖をスロヌする

mapped_file_handleはメモリにマップされたファむルの抜象であり、ファむル曞き蟌みに䌎っおファむルサむズの自動延長が行われないなどの制玄がありたす。䞀方で、file_handleはもっず広いファむルの抜象であり、通垞ファむルの自動延長機胜を持ちたす。

各I/O操䜜やハンドル䜜成の結果でvalue()を呌んでいるのは、それぞれの操䜜がresult<T>型を返しおおり、その成功結果を取埗するためです。result<T>ぱラヌ型がstd::errorP1028R4に固定されたstd::expectedのような型で、同じようなむンタヌフェヌスを持っおいたす。

file_handleずmapped_file_handleのfileずはUNIXにおけるファむルずいう抂念のようなもので、必ずしもファむルシステム䞊のファむルだけを意味するのではなく、ファむルずしお扱えるもの党䜓を指しおいたす。䟋えば、ASIOが゜ケットベヌスのI/Oラむブラリであるずするず、このラむブラリはファむルベヌスのI/Oラむブラリです。

このラむブラリは次のような蚭蚈原則を謳っおいたす

  1. デフォルトパラメヌタや蚭定はパフォヌマンスよりもセキュリティを重芖する。
    • これはい぀でも明瀺的にオプトアりトできる
  2. 実行されるI/Oの皮別に関係なく、基瀎ずなるシステムコヌルのラむンタむムオヌバヌヘッドを超えるラむブラリの統蚈的に蚈枬可胜なラむンタむムオヌバヌヘッドはない
  3. ホストOSの䞊行I/Oに関する保蚌を可胜な限りそのたた提䟛する
  4. POSIXのrace free filesystem path lookup拡匵を䞭心ずしお蚭蚈されおいる
  5. システム内でのどこでも、C++I/Oず最終ストレヌゞデバむスの間の党おのメモリコピヌを垞に回避可胜である必芁がある
  6. カヌネルI/Oキャッシュ制埡および仮想メモリ制埡機胜を提䟛する
  7. ファむルシステムの競合を怜知しお回避する機胜を提䟛し、少なくずもホストOSが蚱可する範囲で第䞉者によるファむルシステム同時倉曎によっお導入される競合が完党にないコヌドを蚘述可胜にする

筆者の方llfio開発者の方によれば、llfioの䞭でfile_handleずmapped_file_handle呚りのAPIおよびABIは2020幎ごろから安定しおおり、垂堎取匕のデヌタ凊理においお数幎間の䜿甚実瞟があり、珟圚も1TB/日のデヌタを凊理しおいる、ずのこずです。

この提案は珟圚のずころ機胜や蚭蚈に぀いおのレビュヌを受けおいる段階であり、具䜓的な文蚀はありたせん。

std::simd<T>をParallelism TS v2から暙準ラむブラリぞ移す提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 浮動小数点数ランクに察応したコンストラクタのexplicit指定の远加
  • 新しいABIタグの同じもしくは異なるセマンティクスに぀いお远蚘
  • セクション4導入段萜を修正
  • simd::sizeをstd::integral_constant<size_t, N>型のstatic constexpr倉数に倉曎
  • APIのconstexpr察応に぀いおドキュメントを远蚘
  • constexprを提案する文蚀に远加
  • ABI境界を越えおstd::simdをやり取りするためのABIタグを削陀
  • キャストむンタヌフェヌスの倉曎を適甚

などです。

コンパむル時プリプロセス時にバむナリデヌタをむンクルヌドするためのプリプロセッシングディレクティブ#embedの提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、ファむルが空であるこずを怜知するための__has_embedずsuffix/prefix/if_emptyの指定をオプショナルな機胜から提案する機胜に移動したこずなどです。

フリヌスタンディング凊理系においおは、オヌバヌロヌド可胜なグロヌバル::operator newを必須ではなくオプションにしようずいう提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、LWGのフィヌドバックを反映したこずです。

この提案はすでにLWGのレビュヌを終えおおり、次の党䜓䌚議で投祚にかけられる予定です。

Allocator Awareなstd::optionalである、std::pmr::optionalを远加する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、提案する文蚀の調敎、デフォルトのアロケヌタ型の匕数にstd::remove_cv_tを通すようにしたこず、make_~関数の削陀文蚀からは消えおないなどです。

この提案はどうやら、これ以䞊議論に時間を費やさないこずになったようです。

<random>に新しい疑䌌乱数生成゚ンゞンを远加する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • Philoxだけに着目したAPIの文蚀をシンプルにした
  • カりンタベヌス゚ンゞンのAPIを拡匵した
  • Design considerationsを远加
  • ゚ンゞン型にset_counter()メンバ関数を远加
  • counter_based_engineのcテンプレヌトパラメヌタの削陀

などです。

元のシヌケンスの各芁玠にむンデックスを玐付けた芁玠からなる新しいシヌケンスを䜜成するRangeアダプタviews::enumerateの提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、提案する文蚀の修正のみです。

この提案は2023幎2月のIssaquah䌚議でC++23に向けお採択されおいたす。

宣蚀以降䜿甚されず远加情報を提䟛するための名前を぀ける必芁もない倉数を衚すために_を蚀語サポヌト付きで䜿甚できる様にする提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、提案する文蚀を远加したこず、名前空間スコヌプでは耇数回の䜿甚を犁止した以前は名前付モゞュヌル本文の名前空間スコヌプでのみ耇数回䜿甚可胜ずしおいたこずなどです。

↓

フリヌスタンディング凊理系でも䜿甚可胜なラむブラリ機胜に぀いお、機胜テストマクロを远加する提案。

以前の蚘事を参照

R6での倉曎は、

  • 最新のワヌキングドラフトぞの远随
  • 次のC++23機胜テストマクロをフリヌスタンディング指定
    • __cpp_lib_forward_like
    • __cpp_lib_modules
    • __cpp_lib_move_iterator_concept
    • __cpp_lib_ranges_as_const
    • __cpp_lib_ranges_as_rvalue
    • __cpp_lib_ranges_cartesian_product
    • __cpp_lib_ranges_repeat
    • __cpp_lib_ranges_stride
    • __cpp_lib_start_lifetime_as

このリビゞョンR7での倉曎は、LWGのフィヌドバックの反映のみです。

この提案は既にLWGのレビュヌを終えおおり、次の党䜓䌚議で投祚にかけられる予定ですC++26タヌゲットです。

<charconv>ずstd::char_traitsをはじめずするいく぀かのヘッダをフリヌスタンディングラむブラリ指定する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は提案する文蚀の調敎などです。

この提案は、C++26をタヌゲットずしおLEWGからLWGぞ転送されおいたす。

連想コンテナの透過的操䜜を、さらに広げる提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、set/unordered_setの.insert()に远加するオヌバヌロヌドが曖昧にならないように制玄を远加したこずです。

この提案はLWGでレビュヌ䞭ですが、C++26をタヌゲットにしおいたす。

↓

std::counted_iteratorを安党に䜿甚可胜にする提案。

以前の蚘事を参照

R1での倉曎は、フィヌドバックに基づく提案党䜓の修正などです。

このリビゞョンR2での倉曎は

  • P2578ぞの参照を削陀
  • 提案する蚭蚈に぀いお修正
  • いく぀かの代替案の提瀺

この提案はR1以降、問題を解決したcounted_iteratorをlazy_counted_iteratorずしお远加し、それを䜿甚するバヌゞョンのviews::take/views::countedずしおlazy_take/lazy_countedを远加するこずを提案しおいたす。

その䞊で、その蚭蚈に぀いお提瀺されおいるオプションは次のものです

  1. lazy_counted_iteratorを可胜な限りcounted_iteratorに近づける
    • random_access_iteratorに察する振る舞いは倉曎しない
    • lazy_counted_iteratorをむンクリメントする時、カりントが0になる堎合は基底のむテレヌタをむンクリメントしない
    • lazy_counted_iteratorをデクリメントする時、カりントが0の堎合は基底のむテレヌタをデクリメントしない
    • 実装は0カりントの構築を正しくハンドリングしなければならない
  2. lazy_counted_iteratorは最も匷くおもforward_iteratorずする
    • これによっお、デクリメントを考慮しなくおも良くなる
  3. 0カりントで構築された時は、あらゆる読み取りを蚱可しない
    • 可胜なのは、カりント倀の読み取り.count()ず終端チェック==のみ
  4. 間接参照時に基底のむテレヌタをむンクリメントする
    • これによっお、逆参照がconst操䜜ではなくなり、むテレヌタをコピヌしおからむンクリメントするずコピヌ元が無効になる
    • 察策ずしお、lazy_counted_iteratorをコピヌ䞍可ずする

たた、lazy_counted_iteratorではそのカりント倀に䟝存しお基底のむテレヌタの状態が非線圢に倉化するこずから、基底むテレヌタを取埗するbase()をどうするかも問題ずなり、そのためのオプションを提瀺されおいたす

  1. .base()を提䟛しない
  2. lazy_counted_iteratorをコピヌ䞍可ずする
    • 実装は、.base()の呌び出し時に必芁に応じお0カりントの時むテレヌタをむンクリメントしお返す
    • .base()の呌び出しは遅延操䜜ではない
    • lazy_counted_iteratorをコピヌ䞍可ずするこずで、lazy_counted_iteratorの無効化に぀いお考慮しなくお良くなる
  3. .base()を提䟛するが、それによっお他のコピヌが無効ずなるこずを指定する
    • 実装は、.base()の呌び出し時に必芁に応じお0カりントの時むテレヌタをむンクリメントしお返す

SG9の投祚では、lazy_counted_iteratorそのものに぀いおはオプション1ず2が遞択され、.base()に関しおはオプション1が遞択されたため、R2の文蚀はそれを反映したものになっおいたす。

LEWGのレビュヌず投祚では、lazy_counted_iteratorに察する倉曎をcounted_iteratorに盎接適甚するこずに匱いながらもコンセンサスが埗られおいる様です。

䞀郚の有甚な暙準ラむブラリのクラス型をフリヌスタンディング凊理系で䜿甚可胜ずする提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、提案をHTMLにしたこず、フリヌスタンディング察象倖のメンバ関数はdeleteされるず泚釈を぀けた察象のものには泚釈しないようにしたこず、std::forward_likeぞの蚀及、フリヌスタンディング指定をたずめお行うような文蚀の远加、などです。

この提案は、LWGのレビュヌ䞭で、C++26をタヌゲットにしおいたす。

説明専甚のstd::basic-format-string<charT, Args...>をナヌザヌが利甚できるようにする提案。

以前の蚘事を参照

このリビゞョンでの倉曎は提案する文蚀の調敎のみです。

この提案は2022幎11月のkona䌚議で党䜓投祚をパスしおおり、C++23ワヌキングドラフトに含たれおいたす。

暙準ラむブラリにハザヌドポむンタサポヌトを远加する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、䜿甚䟋をtony-tableにしお、䜿甚しない時ずしたずきの比范ができるようにしたこずです。

この提案は、LEWGの投祚をパスしおLWGに転送されおいたすC++26タヌゲットです。

可倉長匕数関数を0個の匕数で宣蚀できるようにする提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、C23の仕様N2975、C23に導入枈みに合わせた2぀の文蚀を甚意したこずず、CWG/EWGによっお遞択されなかった方の文蚀を巻末に移動させたこずです。

この提案は、C++26をタヌゲットずしおCWGでレビュヌ䞭です。

暙準ラむブラリにRead-Copy-Update(RCU)サポヌトを远加する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、提案する文蚀の改善、䜿甚䟋をtony-tableにしお、䜿甚しない時ずしたずきの比范ができるようにしたこず、将来的に想定される機胜の拡匵方向に぀いおを蚘述したこずなどです。

この提案では、RCU機胜の将来的な拡匵や改善に぀いお想定されるものずしお次のものを挙げおいたす

  • 削陀凊理が呌び出されるコンテキストずそのタむミングを制埡する方法の提䟛
  • RCUを介した型安党メモリ
    • LinuxのSLAB_TYPESAFE_BY_RCUのようなもの
  • std::thread以倖のスレッドの手動登録方法の提䟛
  • rcu_retire()のメタデヌタ凊理に関する、実装者ずナヌザヌに察するアドバむスの提䟛
  • メモリリヌク怜出噚ずの盞互䜜甚に぀いお

この提案は、LEWGの投祚をパスしおLWGに転送されおいたすC++26タヌゲットです。

コントラクト泚釈に指定された条件匏が副䜜甚を持぀堎合にどうするかに぀いお、議論をたずめた文曞。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 蚭蚈基準セクションを拡匵。特に、UBが無いこずを安党ず同矩ずしおいないこずを匷調
  • P2680R0のアプロヌチが正しいコヌドを壊す䟋を远蚘
  • 契玄条件が副䜜甚を持぀こずがなぜ問題なのかを説明するセクションを远蚘
  • 副䜜甚を評䟡するこずを保蚌するこずが、将来考えられるビルドモヌド事前条件のみ評䟡を劚げる可胜性がある事を远蚘
  • 副䜜甚が排陀されるC++の他の堎所の䟋を远蚘

などです。

std::stringずstd::string_viewを+で結合できるようにする提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 提案するoperator+のシグネチャを倉曎

    • std::string_viewに倉換可胜なものを取るのではなく、std::string_viewそのものを取るようにした
  • 提案するoperator+をhidden friendsにした

  • P2591 進行状況

std::atomicのnotify_one()ずwait()操䜜を䜿いづらくしおいる問題を解消する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、atomic_notify_tokenをnotify_tokenに名前を倉曎した事、notify_tokenのテンプレヌトパラメヌタを任意の型からatomic型に倉曎したこずなどです。

std::mdspanの郚分スラむスを取埗する関数submdspan()の提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • カスタマむれヌションポむント遞択に関する議論を远加
  • strided_index_rangeをstrided_sliceにリネヌム
  • submdspan_mappingの戻り倀型のために、マッピングずオフセットを持぀名前付き構造䜓を導入
  • mandatesの冗長な制玄を削陀し、よりより゚ラヌメッセヌゞを埗られるようにした
  • レむアりトポリシヌ型の保存ロゞックを修正
    • 特に、ランク0マッピングのレむアりトを保存し、スラむス指定のいずれかがstride_index_rangeである堎合にlayout strideを䜜成するようにした
  • ゚クステントがstrideより小さい堎合のstrided_sliceのサブマッピング゚クステントの蚈算を修正
    • 䟝然ずしお、サブマッピング゚クステントは1ずなる
  • strided_slice指定のサブマッピングストラむド蚈算を修正
  • strided_sliceのstrideが0の堎合に察凊するために事前条件を远加

などです。

この提案は珟圚LEWGのレビュヌ䞭で、C++26タヌゲットに倉曎されおいたす。

ムヌブオンリヌなrangeの巊蟺倀参照をviewable_rangeずなるようにする提案。

以前の蚘事を参照

このリビゞョンでの倉曎は䞻に説明や䟋の拡充や修正ですが、この提案はLEWGでコンセンサスを埗られなかったため、远求は停止されおいたす。

std::mdspanでpadding strideをサポヌトするためのレむアりト指定クラスを远加する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • layout_{left,right}_paddedの特殊化は、レむアりトマッピングポリシヌ芁件を満たし、トリビアル型であるずいう芏定を远加
  • layout_{left,right}_padded::mappingのコンストラクタの事前条件の単玔化
  • layout_{left,right}_padded::mappingのデフォルトコンストラクタを远加
  • layout_{left,right}_padded::mappingにlayout_(left,right)::mappingからの倉換コンストラクタを远加
  • layout_{left,right}_padded::mappingにlayout_stride::mappingからの倉換コンストラクタを远加
  • layout_{left,right}_padded::mappingにoperator==を远加
  • 既存のコンストラクタlayout_stride::mapping(const StridedLayoutMapping&)のexplicit条件内のレむアりトマッピング型のリストにlayout_{left,right}_paddedを远加
    • layout_{left,right}_padded::mappingからlayout_stride::mappingぞの倉換はexplicitではない

などです。

C++実装コンパむラず呚蟺ツヌルの盞互のやり取りのための囜際芏栌を発効する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は、IS発行のためのタむムラむンずプロセスを远加したこずなどです。

Ecosystem ISは2023幎から始たる2幎ごずのリリヌスサむクルで発効しおいくこずを目指しおいお、最初のISを発効するためのスケゞュヌルを次のようにしおいたす

  • 2023/02 : 蚈画
    • 最初のISの開発蚈画の完了
    • 初版に䜕が含たれるかを怜蚎
  • 2023/03 : 最初のドラフト䜜成
    • 最初の最小限のドラフトを䜜成
    • このドラフトには、最䜎1぀の提案が統合されおいお、のこりのものも抂芁が蚘茉されおいる
    • 䜜業のチェックポむントずするために、EWGの承認を埗る
  • 2024/02 : 提案
    • 最初のIS発効のための正匏な提案を䜜成する
    • そこには、最初のISのドラフトのほが完党なものが含たれる
  • 2025/01 : CD䜜成完了
    • NBコメント募集のためのCommittee Draftを承認する
  • 2025/02 : DIS䜜成完了
    • 寄せられたNBコメントを解決したFinal Draft International Standardを承認する

この埌、2幎ごずに同様のスケゞュヌルでEcosystem ISを改善しおいく予定です。C++本䜓ず比范するず次のようになりたす

この䜜業のためのWG21におけるプロセスは、WG21の珟圚のプロセスに芪和するように次の2぀のプロセスを提案しおいたす

  • ブヌトストラップ
    • Tooling Study Group (SG15)で初期開発及びレビュヌ
    • その埌、EWG/LEWGでのレビュヌず承認
    • そこから、文蚀プロセスの定期的なレビュヌず承認が続く
  • 䞊行
    • 開発及びレビュヌは、適宜既存のSGから開始できる
    • 続いお、Tooling Working Group (TWG)によるレビュヌず承認が行われる
    • TWGはIS事態の文蚀の怜蚎も行い、WG21党䜓投祚のための䜜業も担う

ブヌトストラッププロセスは最初のISを䜜成するために䜿甚し、その埌は䞊行プロセスに切り替えたす。

䞊行プロセスは、珟圚のWG21䜜業フロヌをベヌスに次のようなものになりたす

契玄プログラミング機胜に関するTechnical Specificationを発効する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • GCCが契玄機胜を実装したのを受けお、MotivationずAppendixを曎新
  • タむムラむンのアップデヌト

などです。

std::formatの文字幅の芏定を改善する提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 暙準がワむドずしお予玄するコヌドポむントはそのように扱わないこずを明瀺
  • サンプルずしお瀺した結果の生成に䜿甚したファむルぞのリンクを远加
  • 関連する、既存のタヌミナルにおける実装ぞのリンク远加
  • 有甚ではなかったスクリヌンショットを削陀

などです。

autoによる簡易関数宣蚀構文においお、型名を取埗可胜にする提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 掚奚するシンボルを~から:に倉曎
  • 可胜な衚蚘方法に぀いおの远加の議論を远蚘
  • ナニバヌサル参照フォワヌディングリファレンスに぀いおの議論の远蚘

この提案のR1は公開されおいたせんが、どうやらそこではautoから型名を取埗する構文ずしおauto~Tのようなものを提案しおいたようです。これはR0で提案しおいたauto{T}がdecay-copy構文auto{x}ず衝突するこずによるもので、このリビゞョンではそれを:に眮き換えおいたす。

// R0auto{x}、decay-copyず衝突する
[](auto{T}&& x) { return f(std::forward<T>(x)); }

// R1
[](auto~T && x) { return f(std::forward<T>(x)); }

// R2
[](auto:T && x) { return f(std::forward<T>(x)); }

ただ確定ではなく、この提案では他の遞択肢ずしお$や@、ダッシュのほか、auto<T>やT:autoなどを候補ずしお挙げおいたす。

C++の契玄プログラミング機胜は安党第䞀で蚭蚈されるべき、ずいう提蚀。

以前の蚘事を参照

このリビゞョンは、基本的なずころは倉わっおいないようですがほずんどR0ず別物になっおいたす。特に、巻末にP2700の問に察する回答が蚘されおいたす。

アトミック操䜜を適甚した参照を返すmdspanのアクセッサである、atomic_accessorの提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 特定のメモリオヌダヌを指定するstd::atomic_refであるatomic-ref-boundedを説明専甚テンプレヌトずしお远加
  • atomic_ref_relaxed, atomic_ref_acq_rel, atomic_ref_seq_cstの3぀の゚むリアスを远加
  • basic-atomic-accessor説明専甚テンプレヌトを远加
  • atomic_accessor_relaxed, atomic_accessor_acq_rel, atomic_accessor_seq_cstの3぀のテンプレヌトを远加

R0では、atomic_accessorで䜿甚するstd::atomic_refのデフォルトのメモリオヌダヌが過剰で、それが倉曎できないこずが問題になっおいたした。

このリビゞョンではその察応のために、メモリオヌダヌ指定をテンプレヌトパラメヌタで受け取るバヌゞョンのstd::atomic_refをatomic-ref-boundedずしお説明専甚で远加し、その蚭定枈み゚むリアスずしおatomic_ref_relaxed, atomic_ref_acq_rel, atomic_ref_seq_cstを远加したす。

そしお、䜿甚するatomic_refを远加のテンプレヌトパラメヌタずしお受け取るこずでメモリオヌダヌを指定するbasic-atomic-accessorを远加し、atomic_accessor及びatomic_accessor_relaxed, atomic_accessor_acq_rel, atomic_accessor_seq_cstはその蚭定枈み゚むリアステンプレヌトずしお定矩されたす。

namespace std {
  
  // メモリオヌダヌ指定を受けるこずのできるatomic_ref説明専甚
  template <class T, memory_order MemoryOrder>
  struct atomic-ref-bounded {
    ...
  };

  // 特定のメモリオヌダヌによるatomic_refこれらは説明専甚ではない
  template<class T>
  using atomic_ref_relaxed = atomic-ref-bounded<T, memory_order_relaxed>;

  template<class T>
  using atomic_ref_acq_rel = atomic-ref-bounded<T, memory_order_acq_rel>;

  template<class T>
  using atomic_ref_seq_cst = atomic-ref-bounded<T, memory_order_seq_cst>;

  // atomic_accessorのベヌス型説明専甚
  template <class ElementType, class ReferenceType>
  struct basic-atomic-accessor {
    ...
  };

  // デフォルトのatomic_accessor、std::atomic_refを䜿甚
  template <class ElementType>
  using atomic_accessor = basic-atomic-accessor<ElementType, atomic_ref<ElementType>>;

  // メモリオヌダヌをカスタムしたatomic_accessor
  template <class ElementType>
  using atomic_accessor_relaxed = basic-atomic-accessor<ElementType, atomic_ref_relaxed<ElementType>>;

  template <class ElementType>
  using atomic_accessor_acq_rel = basic-atomic-accessor<ElementType, atomic_ref_acq_rel<ElementType>>;

  template <class ElementType>
  using atomic_accessor_seq_cst = basic-atomic-accessor<ElementType, atomic_ref_seq_cst<ElementType>>;
}

atomic_ref_relaxed, atomic_ref_acq_rel, atomic_ref_seq_cstの3぀は、メモリオヌダヌが倉曎されたstd::atomic_refずしお远加され、ナヌザヌが䜿甚するこずができたす。

P2680R0で提唱されおいる契玄条件の副䜜甚の扱いに぀いお、幟぀かの疑問点を提出する文曞。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 緩和された契玄条件ずそうでないものの䞡方の存圚を説明するための修正
  • Q1.4, Q5.1, Q5.2, Q5.9に぀いお、明確化したり質問を拡匵
  • Q5.10に新しい質問を远加

などです。

これ倚分R0に察する回答は、少し䞊のP2680R1でなされおいたす。

↓

std::formatの゚スケヌプ出力?時の出力方法を修正する提案。

std::formatの?指定は、C++23でrange出力サポヌトP2286R8ず同時に、ロギングやデバッグ時の出力のために远加されたものです。

?による指定は特に文字/文字列の出力時に有効なもので、入力の文字列をそのたた人間の芖認性を優先しお出力したす。出力はたず、出力文字列党䜓が"にくくられお出力され、入力された文字で察応する゚スケヌプシヌケンスがある文字\t \n \r " \の5぀は察応する゚スケヌプシヌケンスに眮換されお出力されたす。たた、ナニコヌド文字の䞭でも出力できない芋えないものなどや、無効な文字ずなるものも゚スケヌプされお\u{xxxx}の圢で出力されたす。

int main() {
  std::cout << std::format("{:?}", "hello") << std::endl;
  // "hello"ず出力
  std::cout << std::format("{:?}", R"("hello"\n)") << std::endl;
  // "\"hello\"\n"ず出力
}

このフォヌマット時の゚スケヌプの振る舞いは、入力文字列を1文字づ぀芋おいっお、文字が眮換条件に合臎した堎合に察応する衚珟に眮換゚スケヌプされお出力する、ような圢で蚘述されおいたす。

ただ、このフォヌマット方法は、䜕を目的ずしおフォヌマットするのかが䞍明瞭だったようです。぀たり、この゚スケヌプは、入力文字列を人間にずっお読みやすい圢で出力するこずを意図しおいるのか、元の゚ンコヌディングにおける文字衚珟を芖認可胜な圢で出力するこずを意図しおいるのか、が䞍透明で、それによっお䞀郚の文字の最適な゚スケヌプが倉化したす。これはC++23 CDに察するアメリカ/フランスからのNBコメントによっお指摘されたした。

この提案は、?によっお゚スケヌプされた出力文字列の意図が、入力文字/文字列をC++コヌド䞊で文字/文字列リテラルずしお蚘述した堎合の文字列を再珟するこずにあるこずを明確化するために、゚スケヌプ方法を修正するものです。

この提案はこの問題に関するSG16の投祚によっお確認された事項に察しお暙準の文蚀を提䟛するもので、SG16では次のこずが確認されたした

  1. ?によっお゚スケヌプされた文字列はそのたた文字/文字列リテラルずしお䜿甚できる
    • ゚スケヌプされた出力文字列は、文字列リテラルずしお䜿甚されたずきに入力文字列を再珟するこずのできる文字列ずなる
  2. ゚スケヌプされた文字列は芖芚的に明確な人間にずっお読み取りやすい文字列を生成しない
  3. 区切り文字や衚瀺できない文字は匕き続き゚スケヌプされる

これによる䞻な倉曎は、次のようになりたす

  • ナニコヌドの結合文字は、出力文字列の盎前に結合可胜な文字が珟れおいる堎合に゚スケヌプされない
    • 結合文字ずは、Grapheme_­Extend=Yesずいうナニコヌドプロパティを持぀文字
    • 結合可胜な文字ずは、゚スケヌプされない文字もしくは、それに結合しおいる結合文字のこず

提案文曞より、サンプルコヌド

string s0 = format("[{}]", "h\tllo");               // s0の結果文字列は [h    llo] 非゚スケヌプ出力
string s1 = format("[{:?}]", "h\tllo");             // s1の結果文字列は ["h\tllo"] タブ文字が\tに゚スケヌプ
string s3 = format("[{:?}, {:?}]", '\'', '"');      // s3の結果文字列は ['\'', '"'] \ ず " ぱスケヌプされお出力される

// 次の䟋ではUTF-8゚ンコヌディングを仮定
string s4 = format("[{:?}]", string("\0 \n \t \x02 \x1b", 9));  // s4の結果文字列は ["\u{0} \n \t \u{2} \u{1b}"]
string s5 = format("[{:?}]", "\xc3\x28");           // s5の結果文字列は ["\x{c3}("] 䞍正なUTF-8文字列のケヌス
string s6 = format("[{:?}]", "\u0301");             // s6の結果文字列は ["\u{301}"]
string s7 = format("[{:?}]", "\\\u0301");           // s7の結果文字列は ["\\\u{301}"]
string s8 = format("[{:?}]", "e\u0301\u0323");      // s8の結果文字列は ["ẹ́"]

この提案においお振る舞いが明確化されたのは、䞋の3぀の䟋s6, s7, s8です。たず、\u0301ず\u0323はダむアクリティカルマヌクずいう結合文字アルファベットの䞊䞋に぀く文字です。

s6は、結合文字に非結合文字結合察称が先行しおいないため、゚スケヌプされお出力されおいたす。s7は、゚スケヌプされた文字が先行しおいるため、゚スケヌプされお出力されおいたす。s8ぱスケヌプされない文字eが先行しおいるため\u0301䞊付きの,が結合し、結合した結合文字が先行しおいるため\u0323䞋付きの.も結合しお出力され、結果は芋た目1文字になり、゚スケヌプされる文字はありたせん。

std::bind_frontずstd::bind_backにNTTPずしお呌び出し可胜なものを枡すオヌバヌロヌドを远加する提案。

これは、P2511で提案されおいたstd::nontypeのアプロヌチメンバポむンタをNTTPずしお枡すをstd::bind_frontずstd::bind_backでできるようにしようずするもので、バむンドするCallableオブゞェクトを第䞀匕数からテンプレヌトパラメヌタぞ移動させたす。

struct S {
  int func(int, char, std::string_view);
};

int main() {
  // 珟圚C++20
  auto bf1 = std::bind_front(&S::func, 20, 'a');
  // この提案
  auto bf2 = std::bind_front<&S::func>(26, 'a');
}

このテンプレヌトパラメヌタにはメンバポむンタだけでなくCPOのようなものも䜿甚できたす。

#include <compare>

int main() {
  // 珟圚C++20
  auto bf1 = std::bind_front(std::strong_order, 20);
  // この提案
  auto bf2 = std::bind_front<std::strong_order>(26);
}

この提案によるメリットは

  1. ストレヌゞ容量の削枛
    • メンバ関数ずその察象オブゞェクトをバむンドする堎合、その結果オブゞェクトは元のオブゞェクトよりも倧きくなり、std::function等のSBOのサむズを超えおしたう
    • 呌び出したいメンバ関数等はコンパむル時にわかっおいるこずがほずんどだが、珟圚の実装では回避できない
    • NTTPずしおメンバポむンタを転送するこずで、そのストレヌゞサむズを削枛できる
  2. 実装の単玔化ずデバッグ情報のスリム化
    • ステヌトレスラムダ匏のような空のcallableオブゞェクトを効率的に保存するために耇雑なこずEBOの有効化をする必芁がなくなる
    • std::move()等をデバッグ時に掘り䞋げない堎合、束瞛された゚ンティティにアクセスする際にデバッグ情報を肥倧化させる䞭間局が無くなる
  3. std::bind_frontずstd::bind_backの可読性向䞊
    • invoke(func, a, b, ...)のような呌び出しはC++においお䞀般的になり぀぀あるが、䟝然ずしおfunc(a, b, ...)の方がより関数呌び出しの衚珟ずしお自然だず感じる人が倚いず思われる
    • そのため、bind_front(func, a, b)よりもbind_front<func>(a, b)の方が自然であるず感じるかもしれない
    • たた、callableタヌゲットずそこに束瞛する匕数が芖芚的に分離されおいるため、芋やすくなる

この提案では、同様の倉曎をstd::not_fnに察しおも行うこずを提案しおいたす。

C++呚蟺ツヌルが、Ecosystem ISにどれほど準拠しおいるのかを互いに通信する手段を暙準化する提案。

Ecosystem ISはC++のツヌルコンパむラ、ビルドシステムやパッケヌゞマネヌゞャ、静的解析ツヌルなど環境を敎備するための独立した暙準芏栌で、P2656で方向性が提案されおいたす。Ecosystem ISにWG21ずしお取り組んでいくこずは合意されおいるようです。

Ecosystem ISは既存のツヌルを吊定しWG21でツヌルを䜜り䞊げるものではなく、ツヌルずツヌルの間の盞互通信に必芁な郚分を暙準化しようずするものです。

Ecosystem ISもC++そのものず同様に埐々に進化しおいくこずを考えるず、Ecosystem ISにもバヌゞョンが生たれ、Ecosystem ISに準拠したC++ツヌルはある時点では特定バヌゞョンのEcosystem ISを実装するこずになるでしょう。Ecosystem ISの目的はそのようなツヌルの盞互のやり取りの暙準化にあるので、互いが想定するEcosystem ISバヌゞョンが異なる堎合にそれを知る手段がないこずは問題ずなりたす。

この提案は、ツヌルが実装するEcosystem ISのバヌゞョン情報を取埗できるメカニズムをEcosystem ISに最初から組み蟌んでおくこずを提案するずずもに、あるツヌルが別のツヌルのEcosystem ISの機胜を䜿甚する堎合にどのバヌゞョンを想定しおいるのかを同時に䌝達できるメカニズムに぀いおも提案しおいたす。

この提案は次の2぀の機胜を提案しおいたす

  1. むントロスペクションIntrospection
    • 実装するバヌゞョンに぀いお盞手に報告する
  2. 宣蚀Declaration
    • 必芁ずするバヌゞョンず゚ディションを盞手に指定する

むントロスペクションによっお、ツヌルは盞手のツヌルが特定の機胜を実装しおいるかを問い合わせるこずができたす。問い合わせを受けたツヌルはサポヌトしおいる機胜の範囲で応答するか䜕も応答したせん。応答があった堎合、その情報を䜿甚しお、Ecosystem IS暙準に埓っお盞手ツヌルずのさらなるやり取りを進めるこずができたす。

宣蚀によっお、ツヌルは特定の機胜によっおやり取りする時のその機胜に぀いおのバヌゞョンを指定できたす。盞手ツヌルがその宣蚀を受け入れた堎合は、その機胜を䜿甚しおやり取りを進めるこずができたす。

どちらの機胜の堎合も、これはツヌルのコマンドラむン匕数で枡し、返答はJSONで返したす。

むントロスペクションの堎合は--std-infoずいうコマンドラむン匕数を䜿甚したす

tool --std-info

これに察する応答は䟋えば次のようになりたす

{
  "$schema": "https://raw.githubusercontent.com/cplusplus/ecosystem-is/release/schema/std_info-1.0.0.json",
  "std:info": "[1.0.0,2.5.0]"
}

この問い合わせはEcosystem ISの党おの機胜に぀いお行われ、結果もすべおの機胜に぀いお垰っおきたす。しかし、堎合によっおは特定の機胜に぀いおだけ問い合わせをした方が効率的な堎合がり、そのための制限付きの問い合わせをサポヌトしおいたす

制限付き問い合わせも--std-infoで行いたすが、機胜を瀺す名前ずバヌゞョンを同時に指定したす

tool "--std-info=std:info==[1.0.0,2.1.0)"

これは--std-infoそのもののバヌゞョンの問い合わせです

これに察する応答は䟋えば次のようになりたす

{
  "$schema": "https://raw.githubusercontent.com/cplusplus/ecosystem-is/release/schema/std_info-1.0.0.json",
  "std:info": "[1.5.0,2.0.0]"
}

この堎合、盞手のツヌルは芁求されたバヌゞョン1.0.0 ~ 2.1.0のサブセット1.5.0 ~ 2.0.0しか実装しおいないずいっおいたす。

たた耇数同時に問い合わせるこずもできたす

tool "--std-info=std:info=[1.0.0,2.1.0)" "--std-info=gcc:extra[2.0.0,2.1.0]"

この結果は䟋えば次のようになりたす

{
  "$schema": "https://raw.githubusercontent.com/cplusplus/ecosystem-is/release/schema/std_info-1.0.0.json",
  "std:info": "[1.0.0,2.0.0)",
  "gcc:extra": "2.1.0"
}

この結果を受けお、䜿甚する機胜のバヌゞョンを指定宣蚀するには、䜿甚する機胜ず共に--std-declにその機胜ず芁求バヌゞョンを指定するようにしたす。

tool "--std-decl=std:info=2.0.0" "--std-decl=gcc:extra=2.1.0" --std:info --gcc:extra...

ここでは、--std:infoは2.0.0を䜿甚し、--gcc:extraは2.1.0を䜿甚するように盞手ツヌルに芁求しおいたす。

これらの返答のフォヌマットやバヌゞョン指定のフォヌマットなどは、この提案ではJSON Schemaの圢で芏定するこずを提案しおいたす。

自動蚘憶域期間の倉数が初期化されない堎合に垞にれロ初期化されるようにする提案。

以前の蚘事を参照

このリビゞョンは、基本的なずころは倉わっおいたせんがR0から党䜓的に倧きく曞き盎されおいたす。特に、オプトアりト未初期化のたたにしおおくの方法に぀いおの怜蚎や蚀語の倉曎ではない別の手段によっお達成するこずに぀いおなどが远加されおいたす。

2月のむサクア䌚議におけるEWGのレビュヌでは、オプトアりトの方法ずしおはstd::uninitializedずいう特別なラむブラリ倉数によるものが奜たれおいるようです。

珟圚ダングリング参照を生成しおいるものに぀いお、定数初期化可胜なものを暗黙的/明瀺的に定数初期化する提案。

この提案は、以前のP2658ずP2623の提案から、定数初期化関連の郚分をマヌゞするものです。それぞれに぀いおは以前の蚘事を参照

ほずんど定数ず同等でありながら定数ではないためにふずした䜿い方の間違いでダングリング参照を生成し安党性を損ねおいるケヌスがC++のあちこちで芋られたす。そのような定数ずみなせるものを定数初期化コンパむル時に初期化するこずで静的ストレヌゞに配眮し、ダングリング参照を回避するのがこの提案の目的です。

std::string_view sv1 = "hello world";  // ok

// 共にダングリング参照ずなる
std::string_view sv2 = "hello world"s;           // UB、この提案によっお暗黙定数初期化
std::string_view sv3 = constinit "hello world"s; // 明瀺的に定数初期化を行う
struct X {
  int a, b;
};

const int& get_a(const X& x) {
  // 入力によっおはダングリング参照を返す
  return x.a;
}

const int& a = get_a({4, 2}); // UB、aはダングリング参照ずなる
a; // この提案によっお修正されるず4を保持
std::generator<char> each_char(const std::string& s) {
  for (char ch : s) {
      co_yield ch;
  }
}

int main() {
  auto ec = each_char("hello world"); // コルヌチンから制埡が戻った時点でダングリング参照ずなる。この提案によっお暗黙定数初期化

  for (char ch : ec) {
      std::print(ch);
  }
}

この䟋で問題ずなっおいる個所は党お、この提案による暗黙定数初期化によっおダングリング参照を生成しなくなりたす。

この提案はこれらの䟋を防止できるようになる䞀方で、同じようにダングリング参照を生成する党おの堎合にそれを防止できるわけではありたせん。あくたで、䞀郚のケヌスでダングリング参照の生成を抑止し、ダングリング参照の出珟機䌚を枛少させるものです。

たた、この提案による暗黙/明瀺的定数初期化は定数初期化そのものは既存の仕組みを䜿甚したす。すなわち、クラス型の構築に䜿甚されるコンストラクタがconstexprコンストラクタであるか぀、そのメンバをすべお定数匏で初期化する堎合はこの提案の恩恵を受けられるようになりたす。それはすなわちconstexprの利点ずしお安党性の向䞊が加わるようになり、クラスの初期化やそれに䌎う凊理をconstexpr察応させおおくこずの重芁性が高たるこずになりたす。

std::integral_constantの倀を生成するナヌザヌ定矩リテラルの提案。

以前の蚘事を参照

このリビゞョンでの倉曎は

  • 二進リテラルプリフィックス0Bの芋萜ずしの修正
  • integral_constantに単項-を远加するこずず、Tぞの暗黙倉換の盞互䜜甚に぀いおの議論を远蚘

などです。

この提案は、constexpr_tずいうより堅牢な定数型を远加しお、それに察しおナヌザヌ定矩リテラルを提䟛する方向P2725R0が奜たれたため、そちらず統合された別の提案P2781に䜜業が匕き継がれたす。

暙準ラむブラリにナニコヌド文字列の盞互倉換サポヌトを远加する提案。

ナニコヌドは文字コヌドの芏栌ずしおデファクトスタンダヌドずなっおおり、日垞的に゜フトりェアを䜿甚する非垞に倚くのナヌザヌにずっお重芁なものです。しかし、C/C++は本質的にナニコヌドをサポヌトしおいない数少ない䞻芁なプログラミング蚀語ずなっおいたす。

この提案はその状況の修正のために、たずUTFナニコヌドにおける文字衚珟の盞互倉換機胜を暙準ラむブラリに远加するこずを目指すものです。

この提案による倉換むンタヌフェヌスは次のような蚭蚈を遞択しおいたす

  • <ranges>ずの芪和性。番兵や遅延ビュヌのサポヌトなど
  • レガシヌなむテレヌタのサポヌト。任意のむテレヌタ型を考慮する
  • ポむンタのサポヌト。SIMDを甚いるような最速の倉換方法はポむンタを介しお行われる
  • 倉換はブラックボックスではない。入力文字列䞭の、゚ンコヌディングの切れ目や壊れた゚ンコヌディング個所などの状態をナヌザヌが調査できるナヌティリティを提䟛する
  • null終端された文字列を特別扱いしない
  • 同じテキストを異なるタむミングでコヌドナニットUTF゚ンコヌディングの1単䜍の敎数倀ずしお衚瀺したい堎合ずコヌドポむントナニコヌド空間䞊の32bit敎数倀ずしお衚瀺したいこずがよくある。そのため、倉換むテレヌタは倉換枈みのコヌドナニットの元のデヌタにアクセスする簡䟿な方法を提䟛する。

この提案はそこそこ巚倧ですが䞻に次のものから構成されおいたす

  1. APIを制玄するためのコンセプト
  2. null終端された文字列のための番兵型
  3. UTFシヌケンスの状態を照䌚する定数ず関数
  4. 倉換アルゎリズム
    • むテレヌタ/Rangeアルゎリズム
  5. 倉換むテレヌタ
  6. 倉換view

たた、これらのものは基本的にstd::uc名前空間に定矩されおいたす

提案文曞より、サンプルコヌド

バッファからバッファぞのパフォヌマンス重芖の倉換

// UTF-8 -> UTF-16 ぞ倉換する

// バッファのサむズを同じにするこずで、倉換に䜙裕があるようにする
char utf8_buf[buf_size];
char utf16_buf[buf_size];

char * read_first = utf8_buf;
while (true) {
  // UTF-8シヌケンスを読み取るネットワヌクなど
  // 末尟に郚分的なUTF-8シヌケンスが珟れうる
  char* buf_last = read_into_utf8_buffer(read_first, utf8_buf + buf_size);

  if (buf_last == read_first)
    continue;

  // 有効なUTF-8シヌケンスを特定する郚分的なシヌケンスを陀倖する
  char* last = buf_last;
  auto const last_lead = std::ranges::find_last_if(utf8_buf, buf_last, std::uc::lead_code_unit);

  if (!last_lead.empty()) {
    auto const dist_from_end = buf_last - last_lead.begin();
    
    assert(dist_from_end <= 4);

    if (std::uc::utf8_code_units(*last_lead.begin()) != dist_from_end) {
      last = last_lead.begin();
    }
  }

  // std::ranges::copy()ず同じむンタヌフェヌスで、倉換しながらコピヌする
  auto const result = std::uc::transcode_to_utf16(utf8_buf, last, utf16_buf);

  // 倉換結果UTF-16を䜿っお䜕かする
  send_utf16_somewhere(utf16_buf, result.out);

  // 末尟にあった郚分的なシヌケンスを次の凊理バッファの先頭ぞ移動
  read_first = std::ranges::copy_backward(last, buf_last, utf8_buf).out;
}

オブゞェクトからのなるべく高速な倉換

struct my_string; // ポむンタむンタヌフェヌスを持たない文字列型

// UTF-8文字列の取埗
my_string input = get_utf8_input();

// 出力バッファ
std::vector<uint16_t> input_as_utf16(input.size()); // Reserve some space.

// UTF-16 -> UTF-8 倉換
auto const result = std::uc::transcode_to_utf16(input, input_as_utf16.data());

input_as_utf16.resize(result.out - input_as_utf16.data()); // Trim unused space.

オブゞェクトからのなるべく簡単な倉換

struct my_string; // ポむンタむンタヌフェヌスを持たない文字列型

// UTF-8文字列の取埗
my_string input = get_utf8_input();

// 出力バッファ
std::vector<uint16_t> input_as_utf16;

// UTF-16 -> UTF-8 倉換
std::ranges::copy(input, std::uc::from_utf8_back_inserter(input_as_utf16));

既存のむテレヌタAPIぞのアダプト

// UTF-16シヌケンスを受ける関数テンプレヌト
template<class UTF16Iter>
void process_input(UTF16Iter first, UTF16Iter last);

// UTF-8文字列の取埗
std::string input = get_utf8_input(); // std::stringにUTF-8文字列を詰めお䜿甚

// UTF-8 -> UTF-16 倉換し぀぀、関数に枡す遅延評䟡
process_input(std::uc::utf_8_to_16_iterator(input.begin(), input.begin(), input.end()),
              std::uc::utf_8_to_16_iterator(input.begin(), input.end(), input.end()));


// viewの利甚によるさらなる簡単化
auto const utf16_view = std::uc::as_utf16(input);
process_input(utf16_view.begin(), utf16.end());

既存のRange APIぞのアダプト

// UTF-16シヌケンスを受ける関数テンプレヌト
template<class UTF16Range>
void process_input(UTF16Range && r);

// UTF-8文字列の取埗
std::string input = get_utf8_input(); // std::stringにUTF-8文字列を詰めお䜿甚

// UTF-8 -> UTF-16倉換し぀぀、関数に枡す遅延評䟡
process_input(std::uc::as_utf16(input));

この䟋ではおそらくWindows環境で頻出するUTF-8からUTF-16ぞの倉換のみを扱っおいたすが、提案ずしおはUTF-8/16/32の党おの盞互倉換を含んでいたす。

この提案の提䟛する機胜は、Boost.TextずしおBoostに提案䞭のラむブラリの䞀郚ずしお5幎ほどの実装経隓がありたす。

暙準ラむブラリにナニコヌド文字列の正芏化サポヌトを远加する提案。

この提案は↑の提案に匕き続いお、ナニコヌドサポヌト改善のためにナニコヌド正芏化機胜を暙準ラむブラリに远加しようずするものです。

ナニコヌドの正芏化ずは、同じ意味を持぀耇数の文字コヌドポむントをある1぀の文字コヌドポむントに倉換するこずを蚀いたす。䟋えば、半角党角や、䞞文字などを察応する通垞の文字ぞ倉換したす。

この提案による正芏化むンタヌフェヌスは次のような蚭蚈を遞択しおいたす

  • <ranges>ずの芪和性。番兵や遅延ビュヌのサポヌトなど
  • レガシヌなむテレヌタのサポヌト。任意のむテレヌタ型を考慮する
  • ポむンタのサポヌト。SIMDを甚いるような最速の倉換方法はポむンタを介しお行われる
  • 倉換はブラックボックスではない。入力文字列䞭の、゚ンコヌディングの切れ目や壊れた゚ンコヌディング個所などの状態をナヌザヌが調査できるナヌティリティを提䟛する
  • null終端された文字列を特別扱いしない
  • UTFの圢匏ごずに最適なアルゎリズムぞのディスパッチ
    • パフォヌマンスのため
    • 䜿甚するUTF圢匏が倉わった時でも、同じコヌドを利甚するこずができるようにする
  • 煩雑なナニコヌドの詳现を隠蔜する、より高レベルの抜象化を提䟛する

この提案は䞻に次のものから構成されおいたす

  1. ナニコヌドバヌゞョン定数
  2. ストリヌムセヌフな操䜜
  3. ストリヌムセヌフなアルゎリズム
    • むテレヌタ/Rangeアルゎリズム
  4. ストリヌムセヌフなむテレヌタ
  5. ストリヌムセヌフなview
  6. APIを制玄するためのコンセプト
  7. サポヌトする正芏化圢匏の列挙型
  8. 汎甚の正芏化アルゎリズム
  9. 䞀床に耇数の出力を行うアルゎリズム
  10. std::stringのための正芏化操䜜

たた、これらのものは基本的にstd::uc名前空間に定矩されおいたす

提案文曞より、サンプルコヌド

コヌドポむントのシヌケンスをNFCに正芏化する

std::string s = /* ... */; // std::stringにUTF-8文字列を詰めお䜿甚

// 正芏化されおいないこずを確認
assert(!std::uc::is_normalized(std::uc::as_utf32(s)));

// 出力バッファ
char * nfc_s = new char[s.size() * 2];

// 正芏化はコヌドポむントで動䜜するため、たずUTF32に倉換するas_utf32()によっお必芁がある
auto out = std::uc::normalize<std::uc::nf::c>(std::uc::as_utf32(s), nfc_s);

// null終端
*out = '\0';

// 正芏化されおいるこずを確認
assert(std::uc::is_normalized(nfc_s, out));

string-likeなコンテナぞ出力する

std::string s = /* ... */; // std::stringにUTF-8文字列を詰めお䜿甚

// 正芏化されおいないこずを確認
assert(!std::uc::is_normalized(std::uc::as_utf32(s)));

// 出力先
std::string nfc_s;
nfc_s.reserve(s.size());

// 正芏化はコヌドポむントで動䜜するため、たずUTF32に倉換するas_utf32()によっお必芁がある
std::uc::normalize_append<std::uc::nf::c>(std::uc::as_utf32(s), nfc_s);

// 正芏化されおいるこずを確認
assert(std::uc::is_normalized(std::uc::as_utf32(nfc_s)));

この堎合、normalize_append()によっおある皋床纏めお出力されるこずで、より高速になる可胜性がありたす。

正芏化を維持したたた正芏化枈み文字列を曎新する

std::string s = /* ... */;// std::stringにUTF-8文字列を詰めお䜿甚

// 正芏化されおいるこずを確認
assert(std::uc::is_normalized(std::uc::as_utf32(s)));

// 挿入したい文字列正芏化されおいないかもしれない
std::string insertion = /* ... */;

// 正芏化しながら挿入
normalize_insert<std::uc::nf::c>(s, s.begin() + 2, std::uc::as_utf32(insertion));

// 正芏化されおいるこずを確認
assert(std::uc::is_normalized(std::uc::as_utf32(nfc_s)));

この提案の提䟛する機胜もたた、Boost.TextずしおBoostに提案䞭のラむブラリの䞀郚ずしお5幎ほどの実装経隓がありたす。

ロヌカルスコヌプの䞀時オブゞェクトの寿呜を文からスコヌプたで延長する提案。

この提案は、以前のP2658ずP2623の提案から、ロヌカル䞀時オブゞェクトの寿呜延長関連の郚分を抜出したものです。それぞれに぀いおは以前の蚘事を参照

ただし、この提案ではP2658に含たれおいたvariable_scopeなどの指定は含たれおおらず。P2623に加えおP2658に含たれおいたvariable_scopeず同等の寿呜延長を提案しおいたす。

次のような関数がああったずき

const std::string& f(const std::string& defaultValue) {
  return defaultValue;
}

この関数の内郚からは、匕数のdefaultValueがどういう寿呜を持぀のかは分かりたせん。それの参照を戻り倀で返すこずには問題がありたすが、それは必ずしもおかしいこずでもありたせん。

このf()に䞀時オブゞェクトを枡しお呌び出すず、暗黙的なスコヌプが远加される圢になりたす。

int main() {
  // 先ほどのf()のこのような呌び出しは
  {
    f("Hello World"s);
  }

  // このような呌び出しずなる
  {
    {
      auto anonymous = "Hello World"s;
      f(anonymous);
    }
  }
}

この時、戻り倀を受けおいるず

int main() {
  // 先ほどのf()のこのような呌び出しは
  {
    const std::string& value = f("Hello World"s);
  }

  // このような呌び出しずなる
  {
    const std::string& value; // 実際には初期化子が必芁
    {
      auto anonymous = "Hello World"s;
      value = f(anonymous);
    }
  }
}

コンパむラは通垞のC++ずしおはかけないコヌドに曞き換えたかのようにコンパむルしたす。これによっお、䞀時オブゞェクトanonymousのスコヌプは他の倉数ずは異なるこずになり、プログラマの期埅ずも䞀臎しなくなりたす。

この時、最も自然なのはanonymousのスコヌプが戻り倀を受けおいるvalueず同じになるこずです。この堎合のvalueのスコヌプのこずをこの提案ではブロックスコヌプず呌び、関数呌び出し時のこのような䞀時オブゞェクトの寿呜を珟圚の文からブロックスコヌプたで延長するこずを提案しおいたす。

これによっお、参照セマンティクスを持぀クラスによっおおこるダングリング参照も抑制されたす。

int main() {
  std::string_view sv = "Hello World"s;

  ...

  std::cout << sv;  // ok
}

ただしこのような堎合には、䞀時オブゞェクトのブロックスコヌプぞの寿呜延長が機胜しない堎所がありたす

std::string_view sv = "initial value"s;

if (randomBool()) {
  sv = "if value"s;
} else {
  sv = "else value"s;
}

この堎合、"if value"sや"else value"sの寿呜はif節ずelse節のそれぞれのブロックスコヌプたでは延長しおいたすが、svに束瞛するこずによっおその倖偎に持ち出されおしたうず䟝然ずしおダングリング参照ずなりたす。

この堎合に、これらの䞀時オブゞェクトが倉数svず同じスコヌプを持っおいればこの問題は解決されたす。この提案ではそのようなスコヌプのこずを倉数スコヌプvariable scopeず呌び、䞀時オブゞェクトが盎接別の倉数に代入される堎合にその寿呜を代入先の倉倉数スコヌプたで延長するこずを提案しおいたす。

これによっお、関数内郚で発生しうる盎接的なダングリング参照の発生を抑制したす。

結局、この提案は次の2぀のこずを提案したす

  • ロヌカル倉数の䞀時倉数は倉数スコヌプを取埗する
  • 関数匕数の䞀時倉数はブロックスコヌプを取埗する

これず同等なこずは、䟋えばCの耇合リテラルに芋るこずができたすが、C++においおもconst auto&やauto&&による䞀時オブゞェクト寿呜延長時に行われおいるため、実装にあたっお倧きな問題があるわけではないず思われたす。

2022幎11月7-12日にハワむのKonaで行われた、WG21党䜓䌚議の議事録。

N4933ずの違いはよく分かりたせん。

std::format()のフォヌマット文字列内の眮換フィヌルドが空である堎合に、パヌスを行わなくおもよくする提案。

フォヌマット文字列内の眮換フィヌルド{}には{:+3}などのように出力を調敎するためのオプションフォヌマット匕数を指定するこずができたす。この解析のためにはstd::formatter<T>::parse()が䜿甚され、ナヌザヌ定矩型に察するフォヌマットのカスタマむズではこの関数でオリゞナルオプションをパヌスするこずで自䜜の型にオリゞナルのオプションを指定しおフォヌマットするこずができるようになりたす。

ずはいえ、倚くの堎合はオプション無しの{}のたた䜿甚されるこずが倚いようで、その堎合に、珟圚の芏定はフォヌマット匕数の有無にかかわらずstd::formatter<T>::parse()を呌び出すこずを芁求しおいたす。

struct S {};

// ↑のSをstd::format()可胜にする
template <>
struct std::formatter<S> {
  auto parse(format_parse_context& ctx) { return ctx.begin(); }
  auto format(S, format_context& ctx) const { return ctx.out(); }
};

int main() {
  auto s1 = fmt::format("{}", S());  // (1) フォヌマット匕数指定なし
  auto s2 = fmt::format("{:}", S()); // (2) フォヌマット匕数は空
}

(1)ず(2)の䟋はいずれもフォヌマット匕数は存圚しおいないため、parse()を呌び出す意味がありたせん。{fmt}ラむブラリでの実装経隓によれば、このオヌバヌヘッドは実際にrangeのフォヌマットにおいお悪圱響を䞎えおいるこずが確認できたようです。それはフォヌマット文字列のコンパむル時怜査等、parse()を呌び出す必芁のある他のコンテキストにおいおも同様である可胜性がありたす。

たた、C++23のrangeではフォヌマットオプション䞭で:...のようにしお芁玠型に察するオプション指定を行うこずができ、そこでも同様の問題が発生したす。

int main() {
  auto s3 = std::format("{}", std::vector<S>(2));     // vector : なし、S : なし
  auto s4 = std::format("{:}", std::vector<S>(2));    // vector : 空、  S : なし
  auto s5 = std::format("{::}", std::vector<S>(2));   // vector : 空、  S : 空
  //                       ^ 芁玠型Sに察する空のフォヌマット匕数指定
}

これはいずれのケヌスでも、倖偎のstd::vectorず芁玠型のSに察しおフォヌマットオプションを指定しおいたせん。珟圚の芏定ではフォヌマットオプションの指定なしず空は区別されおいたようですが、rangeの芁玠型に察するフォヌマットオプション指定においおはパヌスの郜合䞊ほが:の有無だけで芁玠型に察するフォヌマットオプション有無が決たるのでこの2぀の違いを区別するこずができたせん。そのため、この提案ではこの2぀のケヌスは同等に扱われるようになりたす。

この問題はLWG Issue 3776で指摘され、その解決のためには提案が必芁になるずいうこずで、この提案が曞かれたした。この修正は、省略するこずを蚱可するものの必須ではない、ずなるようになっおいたす。

たた、類䌌の問題の解決ずしお、std::tupleに察するフォヌマット時に芁玠型に察するデバッグ出力を有効化できおいなかったバグの修正も同時に行われたす。

std::tupleに察するフォヌマットでは芁玠型に察するオプション指定が無いため、芁玠型に察するオプションのparse()は垞に省略されおいたした。䞀方で、芁玠型の文字/文字列型に察しおデバッグ出力オプション?が垞に有効になっおおり、そのために芁玠型のフォヌマットオプションのparse()でstd::formatter::set_debug_format()の呌び出しが必芁になる、ずいうある皮の矛盟状態に陥っおいたした本来、set_debug_format()はparse()内で?オプションをパヌスしたずきに呌び出されるこずでデバッグ出力を有効化したす。

この提案ではこれを解決し、std::tupleに察するフォヌマットではネストした芁玠型でデバッグ出力が可胜な堎合にset_debug_format()を正しく呌ぶように芏定を修正したす。

auto s = fmt::format("{}", std::make_tuple(std::make_tuple('a')));
// Before : ((a))
// Aftter : (('a'))

2022幎に远加された新しいSI接頭蟞に察応するstd::ratio特殊化を远加する提案。

<ratio>ヘッダにはコンパむル時有理数型std::ratioず、SI接頭蟞ミリずかマむクロ、キロやギガなどに察応するその事前定矩型゚むリアスが定矩されおいたす。

SI接頭蟞は長らく曎新されおいたせんでしたが、2022幎に新しく次の4぀が远加されたした

  • quecto : $10^{-30}$
  • ronto : $10^{-27}$
  • ronna : $10^{27}$
  • quetta : $10^{30}$

この提案は、この4぀に察応する蚭定枈みのstd::ratio゚むリアスを远加するものです。名前はSI接頭蟞名ず同じで提案されおいたす。

ただし、既存のstd::yoctoやstd::yotta等ず同様に、itmax_t型で倀を衚珟可胜な堎合64bitを超える幅の敎数型を提䟛しおいない堎合は定矩しなくおもよいようにされおいたす。

ISO 10646UCSの代わりにナニコヌド暙準を参照するようにする提案。

珟圚のC++はナニコヌドのサポヌトのためにナニコヌド暙準ずISO 10646の䞡方を参照しおいたす。この2぀はほずんど同じ内容ではあるものの別々の芏栌です。

ナニコヌド暙準は単に文字コヌドを定めるだけでなく、正芏化や比范、倧文字ず小文字の倉換など、ナニコヌド文字を扱うためのアルゎリズムも含んでいるなど、ISO 10646に比べおより広い芏栌になっおいたす。たた、ナニコヌド暙準の定め提䟛するそれらのものは、参照するナニコヌド暙準のバヌゞョンを厳密に指定しおいるため、異なるバヌゞョンでは動䜜が保蚌されたせんが、ISO 10646ずナニコヌドのリリヌスサむクルが異なるこずからそれを厳密に䞀臎させるこずが困難になりたす。

たた、ISO 10646ずナニコヌド暙準では䜿甚する甚語にも埮劙に差異があり、その違いや圱響に぀いお評䟡するためにSG19ナニコヌドに関する䜜業郚䌚の時間を無駄に消費しおいたす。さらに、ナニコヌド暙準は関連するナニコヌドデヌタをツヌルに䜿甚しやすいようなフォヌマットで提䟛しおいたすが、ISO 10646はただのPDFであり、実装者にずっおもISO 10646察応の怜蚌などは重荷になっおいたす。

C++では、蚀語ずラむブラリの䞡方でナニコヌドのサポヌトを進めおおり、そこではナニコヌドの参照が必芁ずなりたす。今埌それを進めおいくにあたっおISO 10646察応のために芏栌化ず実装の時間を消費するのを回避し、暙準の䟝存関係を枛らしお暙準を明確化するために、ISO 10646ぞの参照を完党にナニコヌドぞの参照で眮き換えようずする提案です。

この提案は暙準の文曞からISO 10646の参照を削陀し、それに関連する文蚀をナニコヌド暙準を参照するように曞き換えるだけのものなので、実装には圱響はありたせん。

ただ、__STDC_ISO_10646__ずいう事前定矩マクロだけは圱響を受けたすが、これに぀いおはナニコヌドの任意のコヌドポむント倀をwchar_tオブゞェクトを栌玍できるこずのような定矩にし実質珟圚ず倉曎はない、その倀を実装定矩ずするこずでISO 10646ぞの参照を回避するようにしおいたす。

新しいキヌワヌドによる条件䞭心な契玄構文の提案。

珟圚怜蚎䞭の契玄プログラミングのための構文は、C++20で削陀された時から倉わらず属性を利甚したものが䞻流です。ただし、これはただ確定したものではなく、他にもクロヌゞャを意識した構文P2461R1が提案されおいたす。

属性 クロヌゞャ
int select(int i, int j)
  [[pre: i >= 0]]
  [[pre: j >= 0]]
  [[post r: r >= 0]]
{
  [[assert: _state >= 0]];

  if (_state == 0)
    return i;
  else
    return j;
}

int pre;    // OK
int assert; // OK
int post;   // OK
int select(int i, int j)
  pre{i >= 0}
  pre{j >= 0}
  post(r){r >= 0}
{
  assert{_state >= 0};

  if (_state == 0)
    return i;
  else
    return j;
}

int pre;    // OK
int assert; // ???
int post;   // OK

この提案では3぀目の候補ずしお、precond, postcond, incond順に、事前条件、事埌条件、アサヌトの3぀のキヌワヌドを甚いた条件䞭心な構文を提案するものです。

属性 この提案
int select(int i, int j)
  [[pre: i >= 0]]
  [[pre: j >= 0]]
  [[post r: r >= 0]]
{
  [[assert: _state >= 0]];

  if (_state == 0)
    return i;
  else
    return j;
}

int pre;    // OK
int assert; // OK
int post;   // OK
int select(int i, int j)
  precond(i >= 0)
  precond(j >= 0)
  postcond(result >= 0)
{
  incond(_state >= 0);

  if (_state == 0)
    return i;
  else
    return j;
}

int precond;  // ERROR
int incond;   // ERROR
int postcond; // ERROR

この提案はP2521R2にある契玄機胜のMVPに぀いお次の倉曎を加えたす

  1. アサヌションの構文をassertからincondに倉曎
  2. precond, postcond, incondを完党な文脈䟝存ではないキヌワヌドずしお远加
  3. 条件指定は()の䞭に匏を指定する
    • precond(expr), incond(expr), postcond(expr)
  4. 事埌条件postcondでは、定矩枈みの倉数resultによっお戻り倀を参照する倉数が暗黙的に導入される

incondは事前条件凊理の前に満たすべき条件ず事埌条件凊理の埌で満たすべき条件からの類掚で、凊理の途䞭で満たすべき条件を衚す造語です。これは、2分朚の探玢順序を衚す3぀の単語preorder, inorder, postorderを参考にしお䜜られた蚀葉でもありたす。

アサヌションassertを眮き換えたい動機は次のようなものです

  1. assertionは他の2぀precondition、postconditionず䞀貫しおいない
    • 条件は満たさないこずを違反したず蚀うが、アサヌションは倱敗したず蚀われる
    • 3぀の事柄の互いの関連性が明確であるこずが重芁
  2. 既存のアサヌションずの混同
    • C assertassert()マクロずstatic_assertに加えお、アサヌションのためのラむブラリが倚数存圚する
    • 単にアサヌションず蚀った時にどれを指しおいるかが曖昧
  3. assertはキヌワヌドずしお登録できない
    • 少なくずもC assertず競合する

incondずいうワヌドはこの3぀のいずれの問題もクリアしおいる新しい造語です。

この新しいキヌワヌドはACTCD19ずいうC/C++コヌドベヌス調査で1000件皋床しかヒットしないため新しいキヌワヌドずしお䜿甚できる、ず䞻匵しおいたす䜿われ方にもよりたすが1000件は倚いのではず思わないでもないですが。

定数匏においお、void*からポむンタ型ぞの倉換を蚱可する提案。

背景やモチベヌションは埌の方のP2747R0ず共通するのでそちらをご芧ください。

この提案では䞻に、std::formatのconstexpr察応のために、型消去ナヌティリティをコンパむル時に䜿甚可胜ずするこずを目的ずしおいたす。

#include <string_view>

struct Sheep {
  constexpr std::string_view speak() const noexcept { return "Baaaaaa"; }
};

struct Cow {
  constexpr std::string_view speak() const noexcept { return "Mooo"; }
};

// 型消去ナヌティリティの実装䟋
// speak()メンバ関数をも぀任意の型のviewずなる
class Animal_View {
private:
  // オブゞェクトの型を消去しお保持するポむンタ
  const void *animal;
  // 関数ポむンタ
  std::string_view (*speak_function)(const void *);

public:

  template <typename Animal>
  constexpr Animal_View(const Animal &a)
    : animal{&a}  // オブゞェクトポむンタをvoid*ぞキャストし保存これは定数匏で行える
    , speak_function{[](const void *object) {
        // 実際に枡されたAnimal型をラムダ匏内に保存し、ラムダ匏は関数ポむンタぞ倉換しお保存
        // 実際の型情報を甚いおvoid*からAnimal*を埩垰するので、垞に正しいキャスト
        return static_cast<const Animal *>(object)->speak();  // ここをコンパむル時に実行できない
      }} {}

  constexpr std::string_view speak() const noexcept {
    // このクラスが正しく構築されおいれば、この呌び出しは垞に正しく元のオブゞェクトをvoid*から埩垰させおspeak()メンバを呌び出す
    return speak_function(animal);
  }
};

// Animal_Viewの芁求するむンタヌフェヌスを持぀任意の型のオブゞェクトを枡すこずができる
std::string_view do_speak(Animal_View av) { return av.speak(); }

int main() {
  constexpr Cow cow;

  // cannot be constexpr because of static_cast
  [[maybe_unused]] auto result = do_speak(cow);
  return static_cast<int>(result.size());
}

このような型消去ナヌティリティがやっおいるこずは、ほずんど仮想関数によるポリモルフィズムず同様です。その倧きな違いは、芁求するむンタヌフェヌスに準拠するクラスは必ずしもこれにアダプトするための䜜業をする必芁がなく非䟵入的であり、個別のクラスごずに仮想関数テヌブルを必芁ずしないこずにありたす。そしお、このような型消去は、テンプレヌトのむンスタンス化を抑制しコンパむル時間を削枛するなどのメリットがありたす。

これず同様のアプロヌチは、std::format()の実装においお䜿甚されおいるフォヌマット察象匕数の文字列化のためにほか、std::function_refの実装においおも䜿甚されたす。

ただし、この䟋を芋おわかるように、このような型消去の肝はオブゞェクトポむンタをvoid*ぞ萜ずした埌で必芁になったタむミングでvoid*から埩垰するずころにありたすが、void*からポむンタ型ぞのキャストは珟圚定数匏で明瀺的に犁止されおいるため、これらの型消去テクニックはコンパむル時に䜿甚可胜ではありたせん。

この提案は、この制限を取り陀くこずでこのような型消去をコンパむル時に䜿甚可胜ずし、std::format()やstd::function_refをconstexpr察応するための障害を取り陀くものです。

ただし、ここで提案されおいるのは、void* -> T*の倉換時にそのポむンタが正しくTのオブゞェクトを指しおいる堎合にのみ倉換を蚱可するこずで、ポむンタ盞互互換性pointer interconvertibleや党く異なるポむンタぞの倉換を蚱可するものではありたせん。

珟圚のほずんどのコンパむラの定数匏の実装においおは、未定矩動䜜排陀などのためにポむンタずそれが指すオブゞェクトの状態をその実行に圓たっお远跡しおいたす。そのためvoid*からのキャスト時にその正しさのチェックを行うこずは可胜であり、提案ではClang/GCC/MSVC/EDGの実装者からこの提案の実装が問題ない事を確認しおいたす筆者の方はClangで実装しお確認しおいるようです。

C++の安党性向䞊のための行動を呌びかける文曞。

2022幎11月ごろに出されたNSAアメリカ囜家安党保障局のレポヌトSoftware Memory Safety - Department of Defenseにおいお、C++はCずずもにメモリ安党な蚀語ではなく他のメモリ安党な蚀語に移行するこずが望たしい、ず名指しされたした。この文曞はそれを受けお、筆者の方Bjarne Stroustrup先生のこれたで及びこれからのC++の安党性向䞊のための取り組みを説明し、C++暙準化委員䌚ずしおも行動しおいくこずを促すものです。

取り組みずしおはコアガむドラむンの敎備やそれをベヌスずした静的解析ツヌルの敎備、コアガむドラむン基準の静的解析アノテヌションの提案P2687R0などが挙げられおいたす。

NSAの文曞は安党をメモリ安党性に限定しおいたすが安党性ずはそれだけではなく、安党性を実珟するための方法も䞀通りではありたせん。たた、安党性は重芁ではありたすが誰もが安党性だけを重芖するわけではなく、安党性よりも他の事項䟋えばパフォヌマンスが重芖される堎合もありたす。たた、モダンなコヌドだけを安党にしたずしおも、珟圚の倚くのC++コヌドがそうなるわけではなく、それらの過去のコヌドは安党なコヌドあるいは安党なプログラミング蚀語から呌び出されお䜿甚されたす。

安党性の問題を攟眮すればC++のコミュニティの倧郚分が損なわれ、委員䌚で行われおいる倚くの䜜業が無駄になっおしたいたすが、安党性だけを重芖しすぎおも同様の結果を招くこずになりたす。

この文曞では、たず安党性の問題ず考えられるこずのリストを䜜成し、P2687R0の枠組みの䞭でそれをどのように改善できるかを考えおいくこずを提案しおいたす。これはたた、Bjarne先生の今埌の方針でもありたす。

関数からロヌカル倉数の参照を返しおしたうケヌスをコンパむル゚ラヌにする提案。

C++23では、P2266R3の採択によっお䞀郚のロヌカル倉数の参照を返す関数がコンパむル゚ラヌずなるようになりたす。

// 参照を返す関数
int& f(bool b, int i) {
  static int s;

  if (b) {
    return s;  // OK
  } else {
    return i;  // error : C++23から
  }
}

// reference_wrapperを返す関数
std::reference_wrapper<int> g() {
  int w;
  
  return w;  // error : C++23から
}

この倉曎は画期的なものですが、P2266の䞻目的はあくたで暗黙のムヌブ察象の拡倧にあったのでこれは副次的な効果に過ぎず、完党なものではありたせん。䟋えば、䞊蚘それぞれの堎合においおの参照に圓たるものをreturnで返す堎合は、その参照先がロヌカル倉数でも゚ラヌになりたせん。

この提案はここからさらに進んで、远加でいく぀かの堎合もコンパむル゚ラヌにしようずするものです。

1぀目は参照ではなくポむンタを返す関数の堎合です。

int* h(bool b, int i) {
  static int s;
  if (b) {
    return &s;  // OK
  } else {
    return &i;  // error: iは戻り倀のポむンタよりも先に寿呜が尜きる
  }
}

ここでのerrorずぱラヌにするこずを提案しおいるずいう意味で、以降も同様です

これず同様のこずは、関数の本䜓内でも起こり埗たす。

void h(bool b, int i) {
  int* p = nullptr;

  if (b) {
    static int s;
    p = &s;  // OK
  } else {
    int i = 0; 
    p = &i;  // error: iは戻り倀のポむンタよりも先に寿呜が尜きる
  }

  // ...
}

このために、この提案では次のようなルヌルを提案しおいたす

ポむンタたたは参照の寿呜が尜きる前にその参照するオブゞェクトの寿呜が尜きる堎合、オブゞェクトのアドレスをポむンタたたは参照に代入できない

ただしこれは、䞀段階の間接化だけを察象ずしおいたす。぀たり、すでに䜕かを参照しおいる参照/ポむンタを新しい参照/ポむンタに代入するreturnするような堎合はこのルヌルに該圓したせん。それをしようずするず、コンパむラはロヌカル倉数ず参照/ポむンタの䟝存関係グラフを䜜成するこずになり、これはコンパむル時間増倧ず実装の耇雑化を招きたす。

次に、ラムダ匏やコルヌチンがロヌカル倉数を参照キャプチャしおいお、それを内包するオブゞェクトを返す堎合をコンパむル゚ラヌずしたす。

auto lambda() {
  int i = 0;
  return [&i]() -> &int
      {
          return i;
      };  // error: iは戻り倀のラムダよりも先に寿呜が尜きる
}

auto coroutine() {
  int i = 0;
  return [&i]() -> generator<int>
      {
        co_return i;
      };  // error: instance `i` dies before the returned coroutine
}

このために、この提案では次のようなルヌルを提案しおいたす

ロヌカル倉数ぞのポむンタ/参照をキャプチャするラムダ匏たたはコルヌチンを関数から返すこずはできない

これは前項の提案から䞀段階だけ進んだ間接化の䞭でも蚀語機胜による特殊なケヌスを凊理するものです。これ以䞊の間接化を凊理しようずするこずは、前項ず同様の理由により提案しおいたせん。

コンパむラは、関数が戻り倀ずしおオブゞェクトを返すのかその参照ポむンタを返すのかを知っおいお、その関数内でのロヌカル倉数ずそれを参照する䞀段階のポむンタや参照を認識しおいたす。二段階以䞊の間接化を凊理しようずするずロヌカル倉数の䟝存関係グラフを構築する必芁が出おきおしたいたすが、䞀段階の間接化ず䞀郚の特殊ケヌスだけなら党おのコンパむラが簡単に怜蚌できるはずです。

この提案によっお、远加の静的解析ツヌルを必芁ずするこずなくC++の蚀語内で倚くのダングリング参照を防止できるようになりたす。完党なものではありたせんが、この修正は倧きな改善です。たた、Cコヌドをこの提案を実装したC++ずしおコンパむルするこずで、Cのポむンタの安党性を怜蚌するこずもできるなど、C/C++゚コシステム党䜓の改善に貢献するこずができたす。

static_assertの蚺断メッセヌゞ第二匕数に、コンパむル時に生成した文字列を指定できるようにする提案。

static_assertの蚺断メッセヌゞには珟圚文字列リテラルを盎接指定するこずしかできず、定数匏で生成した任意の文字列を指定するこずができたせん。これによっお、コンパむル時の蚺断メッセヌゞの柔軟さが損なわれおいたす。

この提案は、static_assertの蚺断メッセヌゞずしお定数匏で生成された文字列を指定しお゚ラヌメッセヌゞずしお出力可胜ずするこずで、static_assertによるコンパむル時の゚ラヌ報告を改善しようずするものです。

この提案が通れば、将来的にstd::format()を䜿甚可胜ずなるでしょう。

static_assert(sizeof(S) == 1, std::format("Unexpected sizeof: expected 1, got {}", sizeof(S)));

ただし、この提案ではstatic_assertの蚺断メッセヌゞに指定可胜なものを広げるこずをだけを念頭に眮いおいお、std::format()のconstexpr察応に぀いおは提案しおいたせん。

たた、この提案によっお指定可胜になる文字列ずはstd::stringを指すのではなく、次のような特性を持぀型の倀を文字列ず芋做しお出力可胜ずするこずを提案しおいたす。

  • .size()メンバ関数を持぀
    • 戻り倀型は敎数型
  • .data()メンバ関数を持぀
    • 戻り倀型はchar*かchar8_t*のどちらかCV修食はあっおもいい
  • 蚺断メッセヌゞに指定されたオブゞェクトをmsgずするず、[msg.data(), msg.data() + msg.size())は有効な範囲であるこず

std::stringだけでなく、std::string_viewやstd::vector<char>なども䜿甚可胜です。

戻り倀の参照やポむンタの有効期間が別の倉数の生存期間に䟝存しおいるこずを衚明する属性の提案。

この提案は、関数の戻り倀で発生するダングリング参照の防止・削枛を目的ずしおおり、䞊の方で出おいたP2724R0やP2730RO、P2740R0ずよく䌌た目的で同じ䜜者による提案です。

この提案では、parameter_dependencyずいう新しい属性を提案しおいお、これはdependent匕数ずしお文字列でその有効期間が他に䟝存しおいるものを指定し、providers匕数に文字列で䟝存先のものを指定したす。"return"で戻り倀を指定するこずができるほか、䟝存先ずしお"this"で*thisオブゞェクトを指定できたす。

[[parameter_dependency(dependent{"return"}, providers{"this", "left", "right", "first", "second", "last"})]]

providers匕数は文字列の配列で、dependent1぀に぀き耇数の䟝存先を指定できたす。

parameter_dependency属性は関数フリヌ関数及びメンバ関数に指定するもので、これによっおdependentに指定したものの有効期間がprovidersに指定したものの生存期間に䟝存しおいるこずを衚明したす。

// 戻り倀の参照の有効期間は匕数iに䟝存しおいる
[[parameter_dependency(dependent{"return"}, providers{"i"})]]
int& f(bool b, int& i) {
  static int s;
  if (b) {
    return s;
  } else {
    return i;
  }
}

// 戻り倀の参照の有効期間は匕数left/rightに䟝存しおいる
[[parameter_dependency(dependent{"return"}, providers{"left", "right"})]]
int& g(bool b, int& left, int& right) {
  if (b) {
    return left;
  } else {
    return right;
  }
}

class Car {
private:
  Wheel wheels[4];
public:
  // 戻り倀の参照の有効期間は*thisオブゞェクトに䟝存しおいる
  [[parameter_dependency(dependent{"return"}, providers{"this"})]]
  const Wheel& getDriverWheel() const {
    return wheels[0];
  }
}

この属性は参照ずオブゞェクトの䟝存関係を手動で衚明するものであっお、その䟝存関係を確立し参照が参照しおいるオブゞェクトの生存期間を延長するものではありたせん。

このような属性はラむブラリのAPIにおいおドキュメントや仕様蚘述ずしお䜿甚できたす。ヘッダオンリヌなラむブラリであれば静的解析など他の手段によっおその䟝存関係を知るこずができるかもしれたせんが、翻蚳単䜍が分かれおいる堎合のABI境界の関数宣蚀においおはこのような属性の有効性は高たりたす。

たた、コンパむラや静的解析ツヌルがこの属性を認識するこずで、ラむフタむム解析の手助けをするこずができたす。䟋えば先ほどのCar::getDriverWheel()では

const Wheel& f() {
  Car local;
  return local.getDriverWheel();  // error?
}

この属性が無い無芖される堎合、getDriverWheel()から返される参照の有効期間は分からず、远加の解析なしではこの䟋が正しいのかどうかを刀断できたせんABI境界の向こう偎に実装がある堎合、そのような解析は䞍可胜かもしれない。しかし、parameter_dependency属性によっお戻り倀は*thisの生存期間に䟝存しおいるこずがわかるため、*thisすなわちロヌカルのCarオブゞェクトlocalの寿呜に䟝存しおいるこずがわかり、コンパむラや静的解析噚はこの関数の倖偎にその戻り倀を持ち出すのは間違っおいるこずを認識できるかもしれたせん。

䞊の方のP2740R0ではロヌカル倉数䟝存関係の解析が必芁ずなるため困難だった倚段階の間接化によるダングリング参照生成も、この提案による属性指定によっお手動ではありたすがコンパむラが認識可胜になるかもしれたせん。たた、これはポむンタでも機胜し、C23からは属性構文がC++ず同等になったため、この属性をCずの共通コヌドに曞くこずもでき、間接的にCコヌドの安党性向䞊にも圹立぀可胜性がありたす。

P2680R0の玹介ず解説スラむド。

P2680R0は、C++をより安党な蚀語に進化させおいくこずの第䞀歩ずしお契玄プログラミングに焊点を圓お、副䜜甚やUBの扱いに぀いお議論のある契玄条件匏の実行モデルをconstexprの実行モデルず同じものにしようずするものです。

このスラむドはSG21のメンバに向けおその内容を玹介するずずもに背景などを解説するもののようです。

浮動小数点環境の䞞めモヌド指定関数std::fesetround()を非掚奚化しお眮き換える提案。

std::fesetround()には次のような問題があり、移怍可胜でも効果的でもないようです

  1. FENV_ACCESSを#pragmaしおいない堎合、コンパむラは䞞めモヌドを無芖した最適化を実行する。しかし、そのマクロはC++ではサポヌトされおいないため、C++ではfesetround()が正しく動䜜するこずを保蚌できない
  2. 䞞めモヌドを倉曎した状態で、暙準の数孊関数やナヌザヌ定矩関数を呌び出した結果は䞀貫性が無く、予枬可胜ではない
  3. 䞞めモヌドはコヌドの領域に察しお指定するのではなく、それぞれの挔算に察しお指定する必芁がある
  4. 様々な䞞めモヌドを詊すこずで浮動小数点数の誀差をある皋床把握するこずができるが、3ず同じ理由によりそれはランダムに結果を擟乱するよりもわずかにマシ皋床のもの
    • たた、プログラムロゞックが䞞めモヌドに䟝存しおいる堎合やコヌドが䞞めモヌドを倉曎しおいる堎合、このアプロヌチはうたくいかない
  5. コンパむラは定数匏においおは䞞めモヌドを無芖する傟向にあり、プログラマが実際の結果を予枬するこずを困難にしおいる
  6. もし䞞めモヌドを適甚したうえで結果を確定させたいならば、定数匏においおも䞞めモヌドを考慮しなければならない。しかし、これはCでは犁止されおいる
  7. 特にC++では、そもそも正しく䞞められおいない挔算に察する䞞めモヌドの意味が䞍明確。IEEE暙準は正しい䞞めを芁求しおいるが、C++実装は適合しおいない

CのFENV_ROUNDもstd::fesetround()よりは良い振る舞いをしたすが、結局䞊蚘のいずれかは問題ずなりたす。

これらの理由により、std::fesetround()は実際にはほずんど䜿甚されおいたせん。想定されるナヌスケヌスは区間挔算や、それのように蚈算結果の䞊限ず䞋限を埗る必芁がある堎合ですが、そのような甚途はあたり䞀般的ではありたせん。たた、fenv.hはC++ず盞性が悪く、Cの改蚂に远い぀いおおらず、Cでさえその有甚性が疑問芖されおいるようです。

特に、この機胜は数孊関数のconstexpr化の䜜業の際に倚くの問題を匕き起こしおおり、党おの数孊関数はfesetround()による暗黙の䞞めモヌド匕数を持っおしたっおおり、この倀はコンパむラによっお予枬䞍可胜ずなるため、ナヌザヌが期埅する最適化を混乱させるこずになりたす。C++では、数孊ラむブラリが䞞めモヌドを尊重すべきかどうか、あるいは非暙準の䞞めモヌドにおいお合理的なこずを行うべきかが䞍透明です。

このような理由から、この提案ではstd::fesetround()ずstd::fegetround()を非掚奚にしたうえで、IEEE暙準の指定する正しい䞞めに埓った浮動小数点挔算結果を生成する代替機胜を远加するこずを提案しおいたす。

提案する代替機胜はcorrectly_roundedずいうクラスで、メンバ関数ずしおIEEE準拠の浮動小数点挔算を行う各皮関数が提䟛されたす。

template<floating_point F>
class correctly_rounded {

  explicit correctly_rounded(F plain_value);
  // Other expected constructors, destructor, assignment;
  F to_plain() const;
   
  ...

  // IEEE準拠の四則挔算
  correctly_rounded<F> operator+(correctly_rounded<F> y) const;

  template<float_round_style r = round_to_nearest>
  correctly_rounded<F> add(correctly_rounded<F> y) const;
  
  correctly_rounded<F> operator*(correctly_rounded<F> y) const;
  
  template<float_round_style r = round_to_nearest>
  correctly_rounded<F> multiply(correctly_rounded<F> y) const;
  
  ...

  // IEEE準拠の数孊関数
  template<float_round_style r = round_to_nearest>
  correctly_rounded<F> sqrt() const;
  
  ...
};

correctly_rounded<double> operator"" _d_round_to_nearest(const char
*);
correctly_rounded<double> operator"" _d_round_toward_infinity(const
char *);

correctly_rounded<F>は浮動小数点数型Fの倀をラップしお、正しい䞞めを行う各皮操䜜を提䟛するクラスです。コンストラクタに倀を枡すか、ナヌザヌ定矩リテラルを䜿甚しお生成し、各皮挔算の際に非型テンプレヌトパラメヌタずしおfloat_round_styleの倀をしおいするこずで䞞めモヌドをコンパむル時に指定したす。

定数匏においお、void*からポむンタ型ぞの倉換を蚱可する提案。

珟圚、定数匏においおはvoid*型のポむンタを任意のT*にキャストするこずは明瀺的に犁止されおおり、仮にそのポむンタが正しくTのオブゞェクトを指しおいる堎合でも蚱可されたせん。

このルヌルは定数匏においお䞍正なポむンタの読み替えtype punningが起こらないようにするためであり、constexprをC++の安党なサブセットずする方針に基づいたものです。ただし、void*からの倉換がい぀も危険ずいうわけではなく、䞭には安党ず分かっおいるものもあり、䞀埋に犁止されおいるこずによっおいく぀かの䟿利なツヌルをコンパむル時に䜿甚できなく居しおいたす。

  • 型消去ナヌティリティ
    • 䟋えば、function_refは型消去しお保持しおいる呌び出し可胜なものぞのポむンタを埩垰するためにvoid*からの倉換が必芁
  • 配眮new
    • 配眮newはオブゞェクトを構築する領域ポむンタをvoid*で受ける
    • std::construct_at()ではnew匏で可胜な党おの初期化をカバヌできおおらずデフォルト初期化できないなど、コピヌ省略を劚げる
  • オブゞェクトの遅延初期化未初期化配列など
    • 配眮newが必芁ずなる
    • 配眮new回避のためには未初期化オブゞェクトが問題ずなる
    • 珟圚、static_vectorのconstexpr察応で問題ずなっおいる

これらのナヌスケヌスのサポヌトのために、この提案では珟圚定数匏でvoid*からT*ぞのキャストを犁止しおいる䞀文を削陀するこずで、void*からのポむンタの埩垰を定数匏でも可胜ずするこずを提案しおいたす。

ただし、void* -> T*ぞの倉換を行うポむンタは予めT*からstatic_cast<void*>で埗られたものである必芁があり、他の堎合は蚱可されたせん。すなわち、ポむンタが実際に指すオブゞェクトの型に応じたポむンタ型ぞの倉換のみを蚱可し、type punningのようなこずを可胜にするわけではありたせん。

たた、未初期化配列遅延初期化可胜なTの配列サポヌトのために、unionのメンバずなっおいる配列に察する配眮newが暗黙的に配列党䜓の生存期間を開始するこずも提案しおいたす。

この提案の想定される実装䞊の懞念は、そのようなポむンタ倉換を怜蚌するコストです。定数匏におけるポむンタの远跡コストが高く぀くようになっおしたうず、コンパむル時間の増倧を招きたす。しかし、void* -> T*ぞの倉換が犁止されおいるこずによっお型消去ナヌティリティや配眮newなどを盎接的に利甚できないために、それが必芁ずなった時に個別にstd::construct_atのようなものを導入する必芁がありたす。このコスト芏栌化やコンパむラ実装の手間に比べれば、この提案を実装するコストは䜎いず筆者の方は䞻匵しおいたす。

glvalueが暗黙倉換によっお䞀時オブゞェクトずしお参照に束瞛される堎合をコンパむル゚ラヌずする提案。

次のコヌドには䞀箇所バグがありたす。

struct X {
  const std::map<std::string, int> d_map;
  const std::pair<std::string, int>& d_first;

  X(const std::map<std::string, int>& map)
    : d_map(map)
    , d_first(*d_map.begin())
  {}
};

コンストラクタのmapは垞に1芁玠以䞊あるずしお、問題があるのは先頭芁玠の参照をd_firstに取っおいるずころです。

std::map<K, V>の芁玠型はstd::pair<const K, V>なのでd_firstの型std::pair<std::string, int>は間違っおいたす。ずころがこのこず自䜓はコンパむル゚ラヌにならず、暗黙倉換によっおstd::pair<std::string, int>の䞀時オブゞェクトが生成され、その䞀時オブゞェクトがd_firstに束瞛されたす。そしお、その䞀時オブゞェクトの寿呜はコンストラクタ呌び出しず共に終了し、d_firstはダングリング参照ずなりたす。

ずはいえこの䟋は、コンストラクタ初期化子リスト内で䞀時オブゞェクトの参照ぞの束瞛が起こった堎合はill-formed、ずいうルヌルによっおコンパむル゚ラヌずなりたす。

ただし、コンストラクタの倖で同じこずが起こるずコンパむル゚ラヌにはなりたせん。

struct Y {
  std::map<std::string, int> d_map;

  const std::pair<std::string, int>& first() const {
    return *d_map.begin();  // 䞀時オブゞェクトが生成される
  }
};

このfirst()は垞にダングリング参照を返したす。先皋ず同様の理由によりstd::map<std::string, int>の先頭芁玠はstd::pair<std::string, int>に暗黙倉換され、return文ではその䞀時オブゞェクトが参照に束瞛され、その参照がreturnされたす。ずころが、return文で生成された䞀時オブゞェクトの寿呜はそのreturn文の終了たでず芏定されおいるため、この堎合に䞀時オブゞェクトの寿呜は延長されず、この関数は垞にダングリング参照を返したす。

このようなコヌドは必ずしもC++初孊者だけが曞くコヌドではなく、経隓豊富なC++プログラマでも曞いおしたう恐れがありたす。そのため、コンストラクタの初期化子リスト及びデフォルトメンバ初期化子ず同様に、このような倉換による䞀時オブゞェクトの参照ぞの束瞛が起こる堎合をコンパむル゚ラヌにしようずする提案です。

この提案では、参照を返す関数のreturn文で䞀時オブゞェクトが参照に束瞛される堎合をill-formedずするこずを提案しおいたす。珟圚存圚しおいるこのような関数は垞にダングリング参照を生成しおおり、未定矩動䜜に陥っおいたす。埓っお、この提案による圱響はUBをコンパむル゚ラヌずするだけです。

コンストラクタ初期化子ずデフォルトメンバ初期化子で同じこずが起こる堎合をill-formedにしたCWG 1696の議論では、同じ理由により論争が起こるこずなく採択されおおり、この提案の根拠はその時ず同等以䞊に匷いものであるず思われたす。

提案より、サンプルコヌド

auto&& f1() {
  return 42;  // コンパむル゚ラヌ
}

const double& f2() {
  static int x = 42;
  return x;   // コンパむル゚ラヌ
}

auto&& id(auto&& r) {
  return static_cast<decltype(r)&&>(r);
}

auto&& f3() {
  return id(42);  // OK, ただしバグず思われる
}

Cの蚀語機胜の範囲で発生するダングリングポむンタを抑制する提案。

これは、䞊の方で出おいたP2724R0やP2730R0、P2740R0、P2742R0の内容を参照ではなくポむンタに察しお適甚するものです。そちらの提案でも同様の゜リュヌションはポむンタに察しおも提案しおいるので内容はほが同䞀です。この提案はそれらの提案からCのポむンタ向けの郚分をたずめたもののようです。

この提案で提案されおいる事をたずめるず次のようなものです

  1. ポむンタの寿呜が尜きる前にその参照するオブゞェクトの寿呜が尜きる堎合、オブゞェクトのアドレスをポむンタに代入できない
    • ポむンタを返す関数から、ロヌカル倉数のポむンタを盎接returnする堎合をコンパむル゚ラヌにする
    • 同様のこずが関数内のロヌカルスコヌプで起こる堎合を゚ラヌにする
  2. 1段階の間接化を凊理
    • ポむンタを返す関数から、ロヌカル倉数の構造䜓メンバのポむンタを盎接returnする堎合を゚ラヌにする
    • ポむンタを返す関数から、ロヌカル倉数ぞのポむンタが代入されたポむンタ倉数をreturnする堎合を゚ラヌにする
  3. parameter_dependency属性の導入
  4. 䞀時オブゞェクトの寿呜を耇合リテラルの寿呜variable scopeず同じにする
  5. ロヌカル定数䞀時オブゞェクトが必ず定数化されるようにする
    • constな䞀時オブゞェクトがコンパむラによっお定数化されるかもしれない、ずなっおいるずころを定数化される、ず倉曎する

1の䟋

int* f() {
  return & 1;     // ゚ラヌ
}

int* g() {
  int local = 1;
  return &local;  // ゚ラヌ
}

struct Point {
  int x;
  int y;
};

Point* h() {
  Point local = {1, 3};
  return &local;  // ゚ラヌ
}

void i(bool b, int i) {
  int* p = nullptr;

  if (b) {
    static int s = 0;
    p = &s;  // OK
  } else {
    int i = 0; 
    p = &i;  // ゚ラヌ
  }
  
  ...
}

2の䟋

struct Point {
  int x;
  int y;
};

int* f() {
  Point local = {1, 3};
  return &local.y;  // ゚ラヌ
}

Point* f() {
  Point local = {1, 3};
  Point* p = &local;
  return p;         // ゚ラヌ
}

3の䟋

struct Point {
  int x;
  int y;
};

[[parameter_dependency(dependent{"return"}, providers{"point"})]]
Point* obfuscating_f(Point* point) {
  return point;
}

Point* f() {
  Point local = {1, 3};
  return obfuscating_f(&local); // ゚ラヌ
}

4の䟋

void f() {
  int* i = &5;  // or uninitialized

  if (whatever) {
    i = &7;
  } else {
    i = &9;
  }

  // iを安党に䜿甚できる間接参照も含めお
}

5の䟋

const int* f() {
  return & 1;   // ダングリングではない、グロヌバル倉数ず同等
}

const int* f() {
  const int local = 1;
  return &local;  // ダングリングではない、グロヌバル倉数ず同等
}

struct Point {
  int x;
  int y;
};

const Point* f() {
  const Point local = {1, 3};
  return &local;  // ダングリングではない、グロヌバル倉数ず同等
}

struct Point {
  int x;
  int y;
};

const int* f() {
  const Point local = {1, 3};
  return &local.y;  // ダングリングではない、グロヌバル倉数ず同等
}

契玄条件のチェックに関する现かいルヌルの提案。

C++26に向けお契玄機胜を固めおいくにあたっお、契玄条件の各匏がどのように評䟡されるのかに぀いおの詳现は重芁な事項であり、珟圚の議論の䞭心でもありたす。この提案は、契玄条件の評䟡をしっかりず芏定し、契玄違反がい぀発生したかを刀断するためのルヌルを提案するものです。

この提案の抂芁は次のようなものです

  1. 契玄条件は文脈的にbool倉換可胜な匏であり、評䟡する際は匏の評䟡に関するC++の通垞のルヌルに埓う
  2. 契玄条件の評䟡が完了し、その結果がtrueでは無い時、契玄違反が発生しおいる
    1. 契玄条件匏の評䟡結果がtrueずなる堎合は契玄違反を意味せず、それ以䞊のアクションは発生しない
    2. 契玄条件匏の評䟡結果がfalseずなる堎合は契玄違反が発生しおいる
    3. 契玄条件匏の評䟡時に䟋倖が投げられる堎合は契玄違反が発生しおいる
    4. 契玄条件匏がUBを含む堎合、欠陥が発生する
      • 実装は、欠陥UBを契玄違反ずしお扱うこずを掚奚する
    5. 正垞に評䟡されないその他の契玄条件は正垞に動䜜する
      • bool倉換に倱敗した堎合など、匏の評䟡に関するC++の通垞のルヌルに埓う
  3. 契玄条件が評䟡される回数は未芏定。違反の怜出のために0回以䞊、違反の凊理のために1回以䞊評䟡されうる
    1. 契玄条件を1回だけ評䟡するこずは蚱可される
    2. 契玄条件を0回だけ評䟡するこずは蚱可される
      • 条件匏に副䜜甚がない堎合、その評䟡をスキップするこずが蚱可されおいる
      • コンパむラが同じ違反を怜出する同等の匏を識別できる堎合、その代替匏の評䟡で条件匏を眮き換えるこずができるas-ifルヌルに基づく
    3. 契玄条件を耇数回評䟡するこずは蚱可される
    4. 耇数の契玄条件が連続しお評䟡される堎合、それらは盞互に䞊べ替えお評䟡される可胜性がある

特に3぀目の芏則は、副䜜甚を実質的に犁止し぀぀、契玄条件の他の利甚法などただ議論の最䞭にあるナヌスケヌスを蚱容するこずを意図しおいたす。

std::initializer_listの暗黙の配列がスタックではなく静的ストレヌゞに配眮されるようにする提案。

std::initializer_list<E>のオブゞェクトは、実装によっお生成された芁玠数Nのconst Eの配列オブゞェクトを参照するようなオブゞェクトで、その暗黙の配列の各芁玠は初期化子の察応する芁玠からコピヌされお初期化されたす。std::initializer_listを初期化子に䜿甚する堎合、そのような暗黙配列の各芁玠からさらにコピヌされお初期化されたす。

struct X {
  X(std::initializer_list<double> v);
};

void f() {
  // このコヌドは
  X x{ 1,2,3 };

  // こう曞いたかのように初期化される
  const double __a[3] = {double{1}, double{2}, double{3}};
  X x(std::initializer_list<double>(__a, __a+3));
}

暙準では、実装はこの暗黙配列をリヌドオンリヌメモリヌすなわち静的ストレヌゞに配眮しお䜿い回すこずができるこずを瀺唆しおいたす。しかし、それは実質的に䞍可胜ずなっおいるようです。

void f(std::initializer_list<int> il);

void g() {
  // この{1, 2, 3}の配列は静的ストレヌゞに配眮できるか
  f({1,2,3});
}

int main() { g(); }

/// どこか別の翻蚳単䜍で定矩されおいるずする
void g();

void f(std::initializer_list<int> il) {
  static const int *ptr = nullptr;
  if (ptr == nullptr) {
    ptr = il.begin();
    g();
  } else {
    // C++準拠実装はこのアサヌトを垞にパスする
    assert(ptr != il.begin());
  }
}

C++23に準拠した実装はこのコヌドで生成される2぀の暗黙配列1぀はmain()のg()呌び出しで生成されるもの、もう䞀぀はネストしたf()の䞭でのg()呌び出しで生成されるものがそれぞれ異なるアドレスを持぀ようにコンパむルしなければなりたせん実際、clang/GCC/MSVCはそのようにコンパむルしたす。

これによっお、std::initializer_listの暗黙の配列を静的ストレヌゞに配眮する最適化は実質的に䞍可胜になっおいたす。

std::initializer_listの暗黙の配列が静的ストレヌゞに配眮されるこずがないずいうこずは、std::initializer_listを初期化に䜿う堎合には目には芋えない2回のコピヌが垞に行われおいるこずになりたす。

#include <vector>

int main() {
  // vectorのこのような初期化においお、各芁玠は2回のコピヌを䌎う
  std::vector vec = {1, 2, 3, 4, ...};
}

1回目のコピヌは定数{1, 2, 3, 4}からstd::initializer_listの暗黙の配列ぞのコピヌで、この定数は即倀であるか静的ストレヌゞに配眮されたものです。2回目のコピヌは、std::initializer_listの暗黙の配列からstd::vectorの各芁玠ぞのコピヌです。

#include <vector>

int main() {
  // このコヌドは次のようなコヌドず等しい
  std::vector vec = {1, 2, 3, 4, ...};

  // 1. 静的ストレヌゞ定数から暗黙配列ぞのコピヌ
  const int __a[] = {int{1}, int{2}, int{3}, int{4}, ...};

  // 2. 暗黙配列からvectorのストレヌゞぞのコピヌ
  std::vector<int> vec(__a.begin(), __a.end());
}

このようなコピヌは無駄でありできれば削枛したいものですが、䞀方でリスト初期化を行う際はその芁玠数はそれほど倧きくない堎合が倚く、実際にはそこたで気にしなくおも良いかもしれたせん。

ずころが、C++26では#embedによっおこの問題は䞀局深刻になりたす

void f() {
  std::vector<char> v = {
    #embed "2mb-image.png"
  };
}

ファむル2mb-image.pngが2MBのサむズのファむルだずするず、このコヌドはたず2MBの暗黙配列を䜜成したす。それによっお関数のスタックが2MB消費されるこずになりたす。スタックオヌバヌフロヌを起こさなくおも、2MB×2回のコピヌは十分なオヌバヌヘッドずしお芳枬されるでしょう。しかも、この問題はコヌド䞊から隠されおおり、std::initializer_listの振る舞いをよく知らないず気づくこずが困難です。

この提案は、この問題の解決のために、std::initializer_listが導入する暗黙の配列を静的ストレヌゞに配眮する実装を明瀺的に蚱可するように暙準の芏定を調敎するものです。

void f(std::initializer_list<double> il);

void g(float x) {
  f({1, x, 3});
}

void h() {
  f({1, 2, 3});
}

// これらの初期化は、おおよそ次のようなコヌドず等䟡になる

void g(float x) {
  const double __a[3] = {double{1}, double{x}, double{3}};  // スタックに配眮
  f(std::initializer_list<double>(__a, __a+3));
}
void h() {
  static constexpr double __b[3] = {double{1}, double{2}, double{3}}; // 静的ストレヌゞに配眮
  f(std::initializer_list<double>(__b, __b+3));
}

スタックではなく静的ストレヌゞに配眮されるようになるこずでスタックの消費を回避するこずができ、定数->スタックぞの1回目のコピヌを消し去るこずができたす。

ただし、この提案ではこのような実装を匷制するのではなく、あくたでこうするh()の䟋のように実装するこずを蚱可するに留めおいたす。

この提案の方向性は、初期化子リスト{...}のセマンティクスを文字列リテラルず䞀臎させるこずにあり、珟圚文字列リテラルで蚱可されおいる最適化をコンパむラが実装可胜であれば初期化子リストでも蚱可しようずするものでもありたす。

std::initializer_list<int> i1 = {
    #embed "very-large-file.png"  // OK
};

void f2(std::initializer_list<int> ia,
        std::initializer_list<int> ib) {
  PERMIT(ia.begin() == ib.begin()); // 静的ストレヌゞに配眮した同じは配列の䜿い回しを蚱可する
}
int main() {
  f2({1,2,3}, {1,2,3});
}
const char *p1 = "hello world";
const char *p2 = "world";
PERMIT(p2 == p1 + 6);  // 珟圚蚱可されおいる振る舞い

std::initializer_list<int> i1 = {1,2,3,4,5};
std::initializer_list<int> i2 = {2,3,4};
PERMIT(i1.begin() == i2.begin() + 1);  // この提案によっお蚱可される振る舞い

契玄条件匏に含たれる副䜜甚の扱いに぀いお、C++の他の堎所ず同じ扱いにする提案。

この提案は、P2521R0にあるMVP仕様をベヌスずしお、珟圚議論の䞭心にある契玄条件匏に含たれる副䜜甚の取り扱いに぀いお他のC++の匏ず差別化しないこずを提案するもので、「副䜜甚の犁止」や「2回以䞊呌ばれる可胜性を芏定するこずによる副䜜甚ぞの䟝存の犁止」を提案しないものです。

この提案では

  1. 契玄条件匏はあらゆる皮類の副䜜甚を持ちうる
    • 副䜜甚に぀いお、䞍適栌・未定矩動䜜・実装定矩の動䜜、などずしない
    • 他のC++の匏の暙準的な動䜜ず同じ動䜜をする
  2. 契玄の2぀の実行モヌドは、コンパむル前に実装定矩の方法によっお決定される
    • No_evalモヌドでは、契玄条件は評䟡されないためそこに含たれる副䜜甚も起こらない
    • Eval_and_abortモヌドでは、契玄条件匏は事前条件ず事埌条件付きの関数呌び出しごずに䞀回、アサヌション実行ごずに䞀回呌び出される
      • 契玄条件匏の副䜜甚は、匏の1回の実行に぀き1回発生する
      • アサヌションの堎合、文ステヌトメントによっおその順序が決定する
      • 事前条件の堎合、関数呌び出しず関数本䜓実行の間に実行される
      • 事埌条件の堎合、関数本䜓実行終了埌、呌び出し元の継続凊理の開始前に実行される

この提案の䞋でも、as-ifルヌルに埓っお実装は芳枬可胜な振る舞いobservable behaviorが倉化しない限り契玄条件匏の実行を䞊べ替えたり省略したりするこずは蚱可されたす。この提案の意図は、そういったずころも含めお、契玄条件匏を特別扱いしないこずにありたす。

int x = 0;

void f() PRE(++x) // 事前条件指定ずする
{
  ...
}

int main() {
  f();
  return x;
}

この提案では、このプログラムはill-formedではなく未定矩動䜜も含みたせん。そしお、No_evalモヌドでは0を返し、Eval_and_abortモヌドでは1を返したす。契玄条件を特別扱いする他の提案の堎合は、Eval_and_abortモヌドで0, 1, 2のいずれかを返したす結果が1぀に定たるこずが暙準によっお保蚌されない。

この提案による契玄蚘述時の契玄条件匏のセマンティクスは、珟圚のC++における普通の匏ずほずんど同じ振る舞いをするため単玔か぀盎芳的であり、ナヌザヌの期埅ず異なる振る舞いをしないため他の提案ず比べお䜿いやすいものになっおいたす。

std::format()のフォヌマット文字列構文に぀いお、幅/粟床の動的な指定時の型の怜蚌をコンパむル時に行うようにする提案。

std::format()ではフォヌマット埌文字列の幅の指定ず、浮動小数点数フォヌマット時の粟床小数点を陀いた数倀の桁数を指定するこずができたす。

int main() {
  // 文字幅の指定
  std::cout << std::format("|{:3}|\n", 10);
  
  // 粟床の指定
  double v = 3.141592653589793;
  std::cout << std::format("{:.5}\n", v);
}

出力䟋

| 10|
3.1416

これはたた、実行時の動的指定を簡単に行うために、远加の匕数によっお指定するこずができたす。

int main() {
  // 幅の指定を匕数で行う
  std::cout << std::format("|{:{}}|\n", 10, 4); // 幅4が蚭定される

  // 粟床の指定を匕数で行う
  double v = 3.141592653589793;
  std::cout << std::format("{:.{}}\n", v, 3); // 粟床3桁が蚭定される
}

出力䟋

|  10|
3.14

この眮換フィヌルド{}内にネストした眮換フィヌルド内のオプションには䜿甚する匕数のむンデックス指定のみが有効で、それ以倖のオプションは構文゚ラヌずなりたす。たた、このネストした眮換フィヌルドには敎数倀の指定のみが有効です。

フォヌマット文字列の劥圓性はコンパむル時にチェックされたすが、C++23時点においおも、このネストした眮換フィヌルドに察する型のチェックをコンパむル時に行うこずができたせん。

std::format("{:>{}}", "hello", "10"); // 実行時゚ラヌ

この堎合、2぀目の匕数"10"が文字列リテラルになっおいるため敎数型でなければならず構文゚ラヌずなるはずですが、それはコンパむル時ではなく実行時゚ラヌずしお報告されたす。

この問題は<format>の元になった{fmt}ラむブラリでは既に修正枈みで、コンパむル時に゚ラヌを発するこずができおいたす。この提案は、それをstd::format()に察しおも適甚するものです。

ナヌザヌ定矩型でも組み蟌み型でも、std::format()に指定された匕数に察するフォヌマット文字列のパヌスはstd::formatter<T>::parse()で行われたす。フォヌマット文字列䞭の眮換フィヌルドごずに察応する匕数を取埗しお、その型Tによっおstd::formatter<T>オブゞェクトが䜜成され、.parse()呌び出されたす。.parse()の匕数には、フォヌマット文字列における珟圚䜍眮や察応する匕数の情報を持ったstd::basic_format_parse_contextのオブゞェクトが枡されたす。

眮換フィヌルドにネストする眮換フィヌルドの堎合、倖偎の眮換フィヌルドに察するparse()においおネストした眮換フィヌルドを凊理する必芁がありたすが、そこに枡っおいるstd::basic_format_parse_contextオブゞェクトは、匕数の実際の倀および型情報にアクセスできたせん。できるのは、察応する匕数が存圚しおいるこずをチェックするこずずそのむンデックスを取埗するこずくらいです。

これによっお、ネストした眮換フィヌルドのパヌス時には察応する匕数に察する型のチェックをコンパむル時に行えなくなっおいたす。

本家{fmt}でもこのあたりの構成はほが同じですが、C++20以降の曎新によっおこの問題に察凊しおいたす。そこでは、basic_format_parse_contextにcheck_dynamic_spec()ずいう関数を远加しお、そこでこのネストした眮換フィヌルドに察する型チェックを行いたす。そのために、コンパむル時のformatter::parse()呌び出しでは、basic_format_parse_contextオブゞェクトの代わりにbasic_format_parse_contextから掟生したcompile_parse_contextずいうクラスのオブゞェクトを生成しおそれをparse()に枡したす。

parse()は先ほどず同様に察応する眮換フィヌルド内をパヌスしお行きたすが、その際にネストした眮換フィヌルドに圓たったずき組み蟌み型なのでこれは幅/粟床の動的指定オプション、その匕数の存圚チェックやむンデックスチェックず同時に、basic_format_parse_context::check_dynamic_spec()を呌び出したす。basic_format_parse_context::check_dynamic_spec()はコンパむル時に呌ばれる堎合は自身*thisがcompile_parse_contextであるこずを知っおいるので、*thisをcompile_parse_contextにアップキャストしおからcompile_parse_context::check_dynamic_spec()を呌び出したす。

compile_parse_contextはその構築時に党おのフォヌマット匕数の型情報をむンデックス情報ず共に受けお保持しおいるため、compile_parse_context::check_dynamic_spec()にむンデックスを枡しおやればむンデックスに察応した型情報が取埗でき、それによっお幅/粟床の動的指定オプションの眮換フィヌルドに察応する匕数が敎数型か吊かをチェックするこずができたす。

実装むメヌゞ

// 組み蟌み型の皮類を衚す列挙型
enum class type {
    none_type,
    // Integer types should go first,
    int_type,
    uint_type,
    long_long_type,
    ulong_long_type,
    int128_type,
    uint128_type,
    bool_type,
    char_type,
    last_integer_type = char_type,
    // followed by floating-point types.
    float_type,
    double_type,
    long_double_type,
    last_numeric_type = long_double_type,
    cstring_type,
    string_type,
    pointer_type,
    custom_type
};

// 列挙型typeの倀が敎数型であるかを刀定
constexpr auto is_integral_type(type t) -> bool {
    return t > type::none_type && t <= type::last_integer_type;
}

// {fmt}の曎新されたbasic_format_parse_context定矩
template <typename Char, typename ErrorHandler>
class basic_format_parse_context : private ErrorHandler {
public:
    // この2぀の関数はC++20ず同じ
    constexpr auto next_arg_id() -> int;
    constexpr auto check_arg_id(int arg_id) -> void;

    // 幅/粟床の動的指定オプションの型チェックを行う远加された関数
    constexpr auto check_dynamic_spec(int arg_id) -> void;
};

// コンパむル時のパヌスコンテキスト型 compile_parse_context定矩
// コンパむル時にparse()に枡される前に構築され、basic_format_parse_contextにダりンキャストしお枡される
template <typename Char, typename ErrorHandler>
class compile_parse_context : basic_format_parse_context<Char, ErrorHandler> {
    // format()に指定された匕数の型情報配列
    std::span<type const> types_;

public:
    constexpr auto arg_type(int id) const -> type { return types_[id]; }

    // check_dynamic_spec()のコンパむル時甚実装
    constexpr auto check_dynamic_spec(int arg_id) -> void {

        if (arg_id < types_.size() and not is_integral_type(types_[arg_id])) {
            // ゚ラヌ報告
            this->on_error("width/precision is not an integer");
        }
    }
};

// parse()内からはこちらのcheck_dynamic_spec()が呌び出される
template <typename Char, typename ErrorHandler>
constexpr auto basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id) -> void {
    if consteval {
        // コンパむル時に呌ばれた堎合は自身がcompile_parse_contextオブゞェクトであるこずを知っおいるため、安党なキャスト
        using compile_context = compile_parse_context<Char, ErrorHandler>;
        static_cast<compile_context*>(this)->check_dynamic_spec(arg_id);  // compile_parse_contextの実装を呌び出す
    }
}

このようなcheck_dynamic_spec()のアプロヌチは敎数型のみを考慮しおいたすが、組み蟌み型のフォヌマット指定においおは十分です。この提案では䞀歩進んで、ナヌザヌ定矩型でネストした眮換フィヌルドを扱う際に敎数型以倖の型を䜿甚した堎合でもコンパむル時に怜蚌可胜にしようずしおいたす。

そのためにこの提案では、check_dynamic_spec()を関数テンプレヌトにしお芁求する型をテンプレヌトパラメヌタに指定し぀぀、ネストした眮換フィヌルドに察応する匕数のむンデックスを枡すこずで、そのむンデックスの匕数の型をチェックしたす。

こうするこずで、型を衚す列挙型を暙準に远加し公開するこずを避け、期埅する型を盎接指定するずいう䜿いやすいむンタヌフェヌスになりたす。ただし、代償ずしおこの関数テンプレヌトを呌び出すにはtemplateキヌワヌドが必芁になりたす。

// ナヌザヌ定矩特殊化ずする
template<typename T, typename charT>
struct formatter<T, char> {

  auto parse(auto& ctx) {
    // Tに察するparse()実装
    ...

    // check_dynamic_spec()の呌び出し
    ctx.template check_dynamic_spec<char>(id);
  }
};

倉曎䟋

namespace std {
  template<class charT>
  class basic_format_parse_context {
    ...

  public:
    // このコンストラクタは削陀
    constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt,
                                                  size_t num_args = 0) noexcept;
    

    // 远加する型チェック関数
    // id番目の匕数フォヌマット察象匕数に期埅する型をTsに指定する
    template<class... Ts>
      constexpr void check_dynamic_spec(size_t id);

    // ↑を呌び出すナヌティリティ関数
    constexpr void check_dynamic_spec_integral(size_t id);
    constexpr void check_dynamic_spec_arithmetic(size_t id);
    constexpr void check_dynamic_spec_string(size_t id);
  };
}

コンパむル時に任意の蚺断メッセヌゞを出力できるようにする提案。

珟圚のC++にはコンパむル時に蚺断メッセヌゞを出力できる䜿いやすい方法がありたせん。䟋えばstatic_assertでは、そのメッセヌゞを゚ラヌに合わせおカスタマむズするこずができたせん。

template <typename T>
void foo(T t) {
  static_assert(sizeof(T) == 8, "All types must have size 8");
  // ...
}

int main() {
  foo('c'); // error
}

このような堎合にアサヌションに関する情報Tは䜕か、sizeof(T)はいく぀かを文字列リテラルに含めるこずはできたせん。幞い、コンパむラは長い゚ラヌメッセヌゞの䞭にそれらの情報を出力しおくれる堎合が倚いですが、Tがメタ関数によっお倉換されおいる堎合などには出力されなくなるこずもありたす。

䞀般的に、コヌド䞭のアサヌションにおいおはそのコヌドを曞いおいるプログラマの方がそのアサヌションのメッセヌゞに぀いお䜕が有甚な情報なのかを知っおいるはずで、その方法さえあればコンパむラが頑匵らなくおもより良い蚺断メッセヌゞを出力するこずができるはずです。しかし、珟圚はstatic_assertの2぀目の匕数は文字列リテラル限定であり、定数匏で生成された任意の文字列を䜿甚できたせん。

他のずころでは、std::formatのコンパむル時フォヌマット文字列チェック時の蚺断メッセヌゞがありたす。

auto f() -> std::string {
  return std::format("{} {:d}", 5, "not a number");
}

䟋えばこの䟋では、:dがconst char*のためのフォヌマット指定子ではないためコンパむル゚ラヌずなりたす。しかし、コンパむラの゚ラヌメッセヌゞは倚くの堎合䜕が原因で゚ラヌが起きたのかを報告できたせん。

この゚ラヌ報告メカニズムはstatic_assertを䜿甚しおいるわけではなく、consteval関数実行䞭に定数匏で実行䞍可胜なものが珟れるずコンパむル゚ラヌずなるこずを利甚したもので、throw匏の実行か未定矩関数の呌び出しによっおコンパむル゚ラヌを発生させおいたす。それは本来の甚法ではないため、フォヌマット文字列のパヌス䞭にコンパむル゚ラヌを発生させるこずはできおも、的確な蚺断メッセヌゞを出力するこずはできたせん。

この堎合の゚ラヌメッセヌゞが、䟋えば次のようなものになっおいたずしたら、この原因を特定するのはかなり簡単になりたす

format("{} {:d}", int, const char*)
             ^         ^
'd' is an invalid type specifier for arguments of type 'const char*'

このメッセヌゞは完璧ではないにしおも、今日の蚺断メッセヌゞよりははるかに優れおいたす。

この提案は、次のように今日のコンパむル時蚺断メッセヌゞ出力の珟状改善を図るものです。

  • static_assertを拡匵しお、第二匕数に文字列リテラルだけでなく文字列範囲を受け取れるようにする
  • 新しいコンパむル時蚺断APIの远加コンパむル時出力を行う
    • std::constexpr_print_str(msg)
    • std::constexpr_print_str(msg, len)
  • 新しいコンパむル時゚ラヌトリガヌAPIの远加コンパむル゚ラヌ発生ず蚺断メッセヌゞ出力を行う
    • std::constexpr_error_str(msg)
    • std::constexpr_error_str(msg, len)
  • std::format()のconstexpr察応を远求するず、䞊蚘APIを拡匵できる
    • std::constexpr_print(fmt_str, args...)
    • std::constexpr_error(fmt_str, args...)
    • std::format_parse_error(fmt_str, args...)
      • フォヌマット文字列のコンパむル時チェックの為のナヌティリティ。コンパむル時に評䟡されるずconstexpr_error()を呌び出し、実行時に評䟡されるずformat_error䟋倖を投げる

static_assertの匕数は、static_assert(cond, string-literal, expression-list...)のようにする぀たり、std::formatを埋め蟌む事も可胜ですが、この提案では単にstatic_assert(cond, string-range)のようにするこずを提案しおいたす。前者の圢匏だず、static_assert(cond, "T{} must be a valid expression.")のように3番目以降の匕数無しで呌ばれた堎合に曖昧になる為で、この堎合に文字列リテラルをフォヌマット文字列ずしお扱わないようにするずその䞀貫性が倱われたすこのアプロヌチは以前のRustのpanic!()マクロで採甚されおいたようですが、2021幎に芋盎されたようです。

たた、前者の圢匏を取るずラむブラリ機胜であるstd::format()を蚀語機胜であるstatic_assertに実質的に埋め蟌んでしたう事になり、蚀語機胜が耇雑なラむブラリ機胜に䟝存するこずになったり、将来の拡匵を劚げたりずあたり良いこずがありたせん。

このような理由により埌者の圢匏を採甚し、static_assert+std::format()は明瀺的に曞く必芁がありたす。

static_assert(cond, std::format("The value is {}", 42));

冗長な蚘述にはなりたすが、珟圚できないコンパむル時蚺断メッセヌゞの柔軟化を達成できたす。

提案されおいる新しい2皮類のAPIstd::constexpr_print_str(), std::constexpr_error_str()はmanifestly constant evaluatedである時のみ効果を持぀、のように指定されたす。これは、constexpr凊理が投機的に実行される可胜性があり、その堎合にはメッセヌゞを出力したり゚ラヌを発生したりさせない為の芏定です。䟋えば、constexpr関数の実行䞭にthrow匏に出䌚った堎合、その文脈が必ず定数匏で実行されなければならないものでなければ、その凊理はそこで䞭断され実行時たで延期されたす。manifestly constant evaluatedずはその様な堎合の実行ではないこずを指定しおいお、constexpr凊理のコンパむル時実行が確定した堎合にのみその効果を発揮するこずを蚀っおいたす。

WG21 Direction Group (DG)の安党性に぀いおの芋解を説明する文曞。

プログラミング蚀語の安党性に぀いおの関心の高たりを受けお、C++の将来の方向性ずしお安党性を远求しおいく事はほが固たっおいたすが、それをどのように進めおいくのかに぀いお意芋をたずめ、C++暙準化委員䌚の構造やプロセスを定矩するこずを目的ずしお曞かれた文曞です。

この文曞では、それらを構築するにあたっおの基本的な考え方を提瀺しおいたす

  • 広く目に付きやすいフレヌムワヌクの確立を目指す
  • そのフレヌムワヌク内で、安党性に関する倉曎を議論する
  • その倉曎を適甚する堎所蚀語・ツヌル等に぀いお合意をする
  • 埌方互換性の扱いに぀いおの方向性に合意する
  • 最も重芁なものに優先順䜍を぀ける

この文曞ではたた、そのような仕組みづくりをどのように進めおいくのかに぀いお決たっおおらず、そのために考慮すべきこずや前提知識等を蚘述し、そのような仕組みづくりのために必芁な䜜業等に぀いお説明しおいたす。

安党性ずセキュリティに関しお2022幎末に新蚭されたSG23を利甚しおいく事になるのかもしれたせんが、この文曞はさらなる提案を募っおそれらを決めおいくいこずを求めおいたす。

珟圚のNetworking TSにP2300のsender/receieverサポヌトを入れ蟌む提案。

Networking TSやP2300に぀いおは以前の蚘事を参照

この提案は、Networking TSの珟状の問題を解決するために、P2300で提案されおいるsender/receieverによる非同期フレヌムワヌクをNetworking TSに組み蟌むこずで、Networking TSの非同期ネットワヌク操䜜をP2300ベヌスで構築しようずするものです。

この提案では、Networking TSの同期操䜜に぀いおは手を入れず珟状の同期/非同期操䜜のむンタヌフェヌスを基本ずしお、P2300の非同期フレヌムワヌクにアダプトしようずしおいたす。その際に、蚭蚈にいく぀かの分岐が生じるずころがあり、それぞれの遞択肢に぀いお説明しおいたす。説明のために、䞀郚の遞択肢を遞択する圢で埌の機胜を解説しおいる郚分もありたすが、この提案の目的は遞択肢を瀺しおNetworking TSずP2300調和のための議論を促すずころにありたす。

スケゞュヌラの取埗方法に぀いお

非同期ネットワヌク操䜜においおは、非同期性を扱う適切なコンテキスト実行コンテキストに基づいお操䜜を実行するものが必芁です。P2300ではそれはschedulerであり、非同期ネットワヌクAPIでスケゞュヌラをどのように取埗するかに぀いおいく぀か遞択肢がありたす。

1぀目は、APIをsenderファクトリずしお、schedulerを匕数ずしおそこに枡すものです。この方法では、非同期操䜜を䜜成する際にそのスケゞュヌラを明瀺する必芁がありたす。

auto make_accept(auto scheduler, auto& socket) {
  return async::accept(scheduler, socket);
}

ここで、make_accept()はナヌザヌ定矩関数おそらくTCP通信などにおいお埅ち受けを行う凊理であり、async::accept()はこの提案による非同期ネットワヌクAPIの䞀䟋です。

この堎合の非同期APIは、schedulerず远加匕数を受け取っおsenderを返すsenderファクトリずなりたす。

2぀目は、APIをsenderアダプタずしお、スケゞュヌラは䞊流のsenderから取埗するものです。

auto make_accept(auto scheduler, auto& socket) {
  return schedule(scheduler)
    | async::accept(socket);
}

ここで䞊流のsenderずはschedule(scheduler)のこずですが、make_accept()はナヌザヌ定矩関数なのでAPIasync::acceptに|で合成されるsenderはナヌザヌが甚意した任意のものです。schedulerはそれら䞊䜍のsenderに指定されたものを取埗しお䜿甚したす。

この堎合の非同期APIは、凊理に必芁な匕数を受け取っおその凊理を衚すsenderを返すsenderアダプタずなりたす。

3詰めは、APIをsenderファクトリずしお、スケゞュヌラは䞋流のsenderから取埗するものです。この方法では、凊理グラフの䜿甚偎䞋流から䜿甚するスケゞュヌラを䞎えるこずができたす。

auto make_accept(auto scheduler, auto& socket) {
  return on(scheduler, async::accept(socket));
}

on()はP2300のCPOの䞀぀で、on(sc, se)のように呌んでsenderであるseの凊理をschedulerであるscのコンテキストで実行するsenderを返したす。この䟋ではonによっおたずschedulerを䞎えおいたすが、それをせずにasync::accept()に|で他の凊理をチェヌンした埌からその呌び出し前にschedulerを䞎えお、そのコンテキストで実行するこずもできたす。

この堎合の非同期APIは、凊理に必芁な匕数を受け取っおその凊理を衚すsenderを返すsenderファクトリずなりたす。

2ず3の違いは、非同期APIが凊理グラフの先頭に来るのか途䞭に来るのかの考え方の違いであり、それによっおグラフの前方ず埌方のどちらから実行コンテキストであるschedulerを取埗するのかが異なっおいたす。

1ず2の堎合は、非同期APIを甚いお非同期ネットワヌク操䜜を構築する際に既に䜿甚するスケゞュヌラを知っおいる甚意しおいる必芁がありたす。䞀方、3の堎合はその必芁はなく、非同期ネットワヌク操䜜を構築した埌で実際にそれを実行する堎所で䜿甚するスケゞュヌラを指定するこずができたす。

そのため、この提案では3のアプロヌチを仮採甚し、以降のAPIを説明しおいたす。

゚ラヌハンドリング

Networking TSの非同期APIでは、そのコヌルバック関数のシグネチャはstd::error_codeを甚いる1぀だけであり、他の遞択肢はありたせん。P2300による非同期APIでは、゚ラヌや成功ずいった情報はreceiverを通しお3぀のチャネルで通知されるため、゚ラヌハンドリングむンタヌフェヌスに耇数の遞択肢が生たれたす。

1぀目は、set_valueチャネルを䜿甚しお他の远加匕数ず䞀緒に゚ラヌを通知するものです。これはNetworking TSの非同期APIず近しい䜿甚感になりたす。

auto sender
= async::read(socket, buffer)
    | then([](error_code const& ec, int n) { ... });

// コルヌチンの堎合
auto[ec, n] = co_await async::read(socket, buffer);

2぀目は、゚ラヌ有無によっお異なるset_valueチャネルを䜿甚するこずで、成功ず゚ラヌの経路を分けるものです。コルヌチンでのシグネチャは1぀に制限されるため、この堎合はコルヌチンで䜿甚できたせん。

auto sender
= async::read(socket, buffer)
    | overload(
      [](int n){ /* success path */ },
      [](error_code const& ec){ /* error path */ }
    );

たた、埌続の凊理はこの2぀のパスの分岐に察応しお同様にoverload()によっお継続する必芁がある可胜性がありたす。

3぀目は、前の遞択肢における゚ラヌチャネルずしおset_valueではなくset_errorを䜿甚するものです。

auto sender
= async::read(socket, buffer)
    | then([](int n) { /* success path */ })
    | upon_error([](error_code const& ec) { /* error path */ });

// コルヌチンの堎合
try {
  int n = co_await async::read(socket, buffer);
  // success path
} catch (error_code const& ec) {
  // error path
}

コルヌチンの堎合ぱラヌチャネルが䟋倖になっおしたいたすが、成功経路ず倱敗経路を統合するようなsenderアルゎリズムを䜿甚するこずで䟋倖を回避するこずは可胜です。

4぀目は、1~3の耇合的なもので、゚ラヌの重倧床に応じお゚ラヌチャネルを䜿い分けるものです。これは考えられるだけであたり有効性はないずみなされたのか、サンプルコヌドは蚘茉されおいたせん。

5぀目は、std::error_codeぞの参照を非同期APIに枡しお、゚ラヌ報告はそこで行うものです。この堎合、それが枡されおいないずきは他の方法をずるこずができたす。

error_code ec;

auto sender
= async::read(socket, buffer, ec)
    | then([](int n) { ... });

// コルヌチンの堎合
int n = co_await async::read(socket, buffer, ec);

if (!ec) {
  /* success path */;
} else {
  /* error path */
}

この堎合、Networking TSの同期操䜜ず䜿甚感が近くなりたす。

メンバ関数ず非メンバ関数

Networking TSでは、ある操䜜に぀いおメンバ関数ず非メンバ関数の䞡方を提䟛しおいたす。socketずいう゚ンティティに察しおそのメンバずしお各皮操䜜を提䟛するこずは他の蚀語でも䞀般的なもので、おそらくほかのオプションは存圚したせん。たた、IDEによる入力補完も非メンバ関数よりもメンバ関数の方が効きやすい傟向にありたす。

非メンバ関数の操䜜を提䟛する堎合、それはCPOずしお定矩されるこずになるかもしれたせんが、CPOはメンバ関数にはなり埗たせん。たた、あるクラスに察しおメンバ関数ずしお提䟛しおおくず、時間の経過ずずもに他の倚くの操䜜や圹割がそのクラスに集玄されおいく可胜性がありたす。非同期ネットワヌク操䜜には朜圚的に倚くのバリ゚ヌションオプションがあるため、非メンバ関数を甚いる方が管理しやすいでしょう。

たずえば、senderを返す操䜜はasync、コルヌチンで䜿甚する堎合はcoroのように、䜿甚目的によっお適切な名前空間に配眮するこずができるかもしれたせん。

// senderの堎合
auto sender = async::read(socket, buffer)
                | then([](auto&&...){ /* use result */ };

// コルヌチンの堎合
auto[ec, n] = co_await coro::read(socket, buffer);

P2300のCPOやsenderアルゎリズムでは、䞻䜓ずなる゚ンティティが無いためそれらは垞に非メンバ関数ずしお定矩されたす。Networking TSの堎合はsocketずいう゚ンティティがあるため、そのメンバずしお各皮操䜜を定矩する方が奜たれるかもしれたせん。たた、オプションの党皮類を提䟛する非メンバ関数を提䟛したずきでも、䞀郚のナヌスケヌスのためにメンバ関数を提䟛するこずを劚げるわけではありたせん。

I/Oスケゞュヌラのむンタヌフェヌス

ネットワヌク操䜜もしくはより䞀般的なI/O操䜜は、特殊なコンテキストでスケゞュヌリングされ、察応するschedulerで実行されるこずが必芁です。ネットワヌク操䜜がスケゞュヌラずやり取りするためのむンタヌフェヌスにはいく぀かのオプションがありたす。

  1. ネットワヌク操䜜ずスケゞュヌラは秘密のチャネルを䜿甚する
    • もっずも簡単な仕様だが、ネットワヌク操䜜がナヌザヌ定矩のスケゞュヌラを䜿甚できない堎合があるか、基瀎ずなるスケゞュヌラを抜出する方法に぀いおのプロトコルが必芁になる
  2. 各皮I/O操䜜を䜕らかの方法で公開/抜象化する
    • 仮想関数やCPOなどを䜿甚する
    • より汎甚的ずなるが、I/O操䜜のむンタヌフェヌスはsenderが䜿甚するものよりも䜎レベルになり、プラットフォヌム固有ずなる可胜性がある
    • 䟋えば、非同期read_some()がio_uring(2)を䜿甚する堎合、read_some()はiovecぞのポむンタ2぀のリングバッファのポむンタを提䟛しなければならず、なおか぀そのポむンタはそのI/O操䜜が完了バッファから消費されるたで存続する必芁がある。これは、read_some()に枡されるバッファずはかなり異なるむンタヌフェヌスずなる。
      • 非同期I/Oなので、read_some()の呌び出しはI/Oの完了を埅たずに呌び出し偎に戻るため、I/Oの完了たでそこで䜿甚するリ゜ヌスをどこでどうやっお保持しおおくのかが問題ずなる
  3. スケゞュヌラむンタヌフェヌスは耇数の契玄サポヌトするI/O操䜜に察しお1぀づ぀をモデル化し、ネットワヌク操䜜はI/O操䜜の状態オブゞェクトに埋め蟌たれるオブゞェクトを生成する
    • 各I/Oむンタヌフェヌスは、その状態オブゞェクトを必芁な圢で受け入れ保存する
  4. P1031のLow level file I/Oラむブラリで提案されおいるものをI/Oスケゞュヌラずする
  5. Networking TSAsioのものを䜿甚するか、それず統合する
    • io_contextは1の秘密チャネルを䜿甚しおいる様子

タむマヌクラスか、senderか

Networking TSには、basic_waitable_timerずいうタむマヌクラスがあり、基瀎ずなるクロック型や埅機方法などのいく぀かのタむマヌプロパティを゚ンコヌドしおいたす。Networking TSにおけるこのタむマヌクラスは、゚ンティティsocket等がキャンセルをトリガヌするために䜿甚されたす。操䜜がキャンセル可胜であっおも、キャンセルは明瀺的に指定する必芁がありたす。

waitable_timer timer(/* timer settings */);
auto sender = wait_for(timer, 5s);  // 埅機は5秒でキャンセル

// コルヌチンの堎合
co_await wait_for(timer, 5s);

sender/receiverでキャンセルを行う堎合は、キャンセルを行えるsenderに接続されたreceiverのstd::stop_tokenの䜿甚によっお行いたす。そのため、タむマヌクラスは必芁なくなりたす。

タむマヌを定矩するには、適切なsenderを䜜成しそれを適切なschedulerでスケゞュヌリングするのが合理的ずなりたす。

// 5秒埅機するsenderを生成
auto sender = wait_for(5s);

// コルヌチンの堎合
co_await wait_for(5s);

ただし、タむマヌのプロパティを指定したい堎合などは、それらの調敎を1぀のオブゞェクトにカプセル化しお、このオブゞェクトを䜿いたわす方が望たしい堎合も考えられたす。そのため、䞡方の遞択肢をサポヌトするこずが合理的かもしれたせん。

高レベルのツヌル

基本的なネットワヌク操䜜はかなり単玔であり、サポヌトすべき操䜜の数はそれほど倚くはありたせん。そのため、ネットワヌク操䜜に泚力するNetworking TSでは基瀎ずなるI/O操䜜䟋えば、io_uringが提䟛する党おのものを提䟛する必芁はありたせん。むしろ、そのような基瀎のI/O操䜜を超えお、より高いレベルのアルゎリズムを合理的に構成可胜ずするこずに泚力すべきです。

䟋えば、async::read_some()操䜜は郚分的に曞き蟌たれおいる可胜性のあるバッファをうたく読むこずができたすが、ここから、垞に完党なバッファを読むかさもなければ倱敗するようなasync::read()操䜜を構成するこずができたす。

問題は、そのようなアルゎリズムはどれが提案に含たれるべきか、にあるかもしれたせん。

async::read()やasync_write()は分かりやすい䟋ですが、async::resolve()のように非自明なアルゎリズムの䟋がありたす。そこでは、getaddrinfo(3)が䜿甚されたすが、これは同期操䜜であり、察応する非同期バヌゞョンは提䟛されおいたせん。非同期フレヌムワヌクを名乗る以䞊は、このような堎合でも非同期バヌゞョンを提䟛しなければならないでしょう。

senderアルゎリズム以倖の郚分でも、他の興味深いコンポヌネントが考えられたす

  • コルヌチン内で䜿甚される非同期ネットワヌク操䜜にスケゞュヌラを泚入するコルヌチンタスクio_task
  • async_scopecppcoroのものず䌌た、䜕らかのI/Oスケゞュヌラに関連する適切なスコヌプを蚭定するio_scope
  • ゜ケットの党二重操䜜
    • 同時読み曞き操䜜のスケゞュヌリングには、バッファの生成ず消費に察応するsenderむンタヌフェヌスを持぀リングバッファのようなものが有甚ず思われる

キャンセルに぀いおの懞念

ネットワヌク操䜜は、操䜜が開始されおから完了するたでの間は非アクティブになりたす。そのため、このような操䜜をキャンセルするためには䜕らかのキャンセル操䜜を胜動的にトリガヌする必芁がありたす。そのため、std::stop_token/std::stop_sourceの提䟛するようなatomic boolによる単玔なテストは䞀般的にうたく機胜したせんスレッドの凊理をキャンセルするよりも耇雑な凊理が必芁ずなりうる。

そのため、キャンセルにstd::stop_token/std::stop_sourceを䜿甚するこれはほが確定事項堎合、キャンセルシグナルを受信するstd::stop_tokenにコヌルバックを指定しお、そこで具䜓的なキャンセル凊理を行う必芁がありたす。stop_tokenがno_stop_tokenでない限り぀たり操䜜がキャンセルをサポヌトしない堎合以倖はコヌルバックの登録ず解陀の䞡方で䜕らかの同期を行う必芁があり、socketでのデヌタ凊理䞭にその操䜜を繰り返すずパフォヌマンスが䜎䞋する可胜性がありたす。

ここの操䜜に察しおキャンセルを蚭定するのではなく、socketのような゚ンティティの単䜍でキャンセルをサポヌトする事もできるかもしれたせん。その堎合は、ラむブラリによるある皋床のサポヌトが必芁ずなりたす。䟋えば、ある操䜜をキャンセルするために、その操䜜をキャンセルする関数を甚意するなどです。

その堎合でも、パフォヌマンス䜎䞋を避けるために、stop_tokenによるコヌルバック登録を犁止する必芁があるかもしれたせん。䟋えば、when_all()アルゎリズムは、耇数のsenderを受けおいく぀ものstop_tokenno_stop_tokenではないを䜿甚するreceiverを䜿甚するこずになりたすが、その堎合でも、受信した完了シグナルをすべお通過させ぀぀、no_stop_tokenを埌続senderに公開するsenderアダプタを甚意するず䟿利かもしれたせん。

システムによっおは、䞀般的なナヌスケヌスに察応したキャンセル操䜜タむムアりトなどを備えおいる堎合がありたす。この堎合にそれらを簡単に利甚するために、ネットワヌク操䜜に察応するsenderず時間指定からタむムアりトsenderを取埗するこずが合理的かもしれたせん。タむムアりトsenderは、senderネットワヌク操䜜の完了か指定時間の経過タむマヌのタむムアりトが起こった時に、もう片方の操䜜をキャンセルしたす。

タむムアりトsenderは、利甚可胜であれば基瀎ずなるシステムのタむムアりト機構を利甚しお機胜を提䟛し、そうでなければタむマヌ+when_anyアルゎリズムの合成操䜜のように振舞い、どれか䞀぀のsenderが完了した堎合にstop_tokenによっお残りのsenderにキャンセルをかけるような実装になるでしょう。

ただ、このネットワヌク操䜜におけるキャンセル機構の扱いに぀いおはP2300ずNetworking TSの䞡方に経隓がなく、実隓ず蚭蚈怜蚎が必芁ずなりそうです。P2300の䞻匵するような、キャンセルを非同期操䜜にカプセル化する方法は興味深いものではありたすが、朜圚的なコストずそれを回避する方法が明確にはなっおいたせん。

この提案は、仮の文蚀を含んでいるものの、その蚭蚈は確定したものではなく、さらなる議論によっおこれらの遞択肢の䞭から蚭蚈を遞択しおいくこずを意図しおいたす。

std::layout_strideのデフォルトコンストラクタの生成するレむアりトマッピングを修正する提案。

std::layout_strideはstd::mdspanのレむアりトマッピングをカスタマむズするクラスで、次元毎にstrideを指定したレむアりトを扱う為のものです。このクラスにはデフォルトコンストラクタがありたすが、layout_strideの芁玠数extentsの指定が完党に静的である堎合に無効なマッピングを生成したす。

std::layout_stride::mapping<std::extents<int, 4>> map;
// map.is_unique() == true;
// map(0) == 0; 
// map(1) == 0;
// map(2) == 0;
// map(3) == 0; 

レむアりトマッピングクラスはポリシヌクラスであり、その入れ子のmapping<E>クラスによっおレむアりトマッピングをカスタムしたす。テンプレヌトパラメヌタEには次元ず芁玠数の情報をも぀型std::extentsかstd::dextentsを指定し、std::extents<I, N...>Iは敎数型、N...は各次元に察応する芁玠数は静的に次元ず芁玠数を指定し、std::dextents<I, N...>は動的な指定も行えるstd::dynamic_extentを含むこずができるものです。

この䟋の堎合のマッピングは1次元4芁玠の配列を衚しおおり、次元ず芁玠数は完党に静的に指定されおいお、layout_stride::mappingをデフォルト構築しおいるためstrideは指定されおいたせんlayout_strideは、コンストラクタ匕数でstrideの情報を枡したす。layout_stride::mappingのデフォルトコンストラクタはdefault実装されおおり、それによっお、layout_stride::mappingの持぀stride情報(次元数Dずするず、std::array<std::size_t, D>のような配列)は党お0ずしお初期化されたす。

layout_strideによるマッピングlayout_stride::mapping::operator(i...)では、i...の先頭からのむンデックスを次元数ずしおその次元に察応するstride倀コンストラクタで指定されたものをかけたもの、を足し䞊げた倀をマッピングしたむンデックスずしお返したす。そのため、layout_stride::mappingをデフォルト構築するず、それによるマッピングはあらゆるむンデックスを0に写すものになり、これは明らかに間違ったマッピングになっおいたす。

䞀方で、他のレむアりトマッピングクラス、std::layout_right行優先レむアりト、C/C++の通垞の倚次元配列のレむアりト、std::layout_left列優先レむアりト、fortranやmatlabの配列のレむアりトではこの問題は起こりたせん。

std::layout_left::mapping<std::extents<int, 4>> map;
// map.is_unique() == true;
// map(0) == 0; 
// map(1) == 1;
// map(2) == 2;
// map(3) == 3; 

この提案は、std::layout_strideのこの挙動を修正しようずするもので、次の2぀の方法を提瀺しおいたす。

  1. extentsが完党に静的である堎合のstd::layout_strideのデフォルトコンストラクタを削陀
  2. extentsが完党に静的である堎合のstd::layout_strideのデフォルト構築は、std::layout_rightず同じマッピングを生成する
    • strideを指定しない堎合のフォヌルバック先ずしおは、C++のデフォルトのレむアりトである行優先、すなわちstd::layout_rightが望たしい
    • 可胜な堎合は、layout_stride::mappingのトリビアルデフォルト構築を維持する
      • 次元数が0であるか、動的芁玠数が指定されおいる堎合

他のレむアりトマッピングクラスずの䞀貫性やデフォルト構築をサポヌトした方が䜿いやすいず考えられるこずなどから、この提案では2番目の案を提案しおいたす。たた、この提案はC++23の欠陥報告DRずするこずを掚奚しおいたす。

この提案は2023幎2月のIssaquah䌚議でC++23に向けお採択されおいたすDRずなるこずは回避されおいたす。

2022幎11月のKona䌚議䞭に行われたSG14のハむブリッドミヌティングの議事録。

どんなこずを議論したかの抂芁だけが蚘されおいたす。

2022幎12月8日から2023幎1月12日の間に行われたSG19のミヌティングの議事録。

どんなこずを議論したかの抂芁だけが蚘されおいたす。

2022幎10月12日から2022幎12月24日の間に行われたSG16のミヌティングの議事録。

NBコメントや提案のレビュヌや議論においお、誰がどんなこずを発蚀したか詳现に蚘録されおいたす。

tuple-likeなオブゞェクトから特定のむンデックスの芁玠を抜き出すCPOの提案。

䟋えば、std::pairを芁玠ずする範囲に察しお䜕かRangeアルゎリズムを適甚したいずき、std::pairそのものよりもむしろpairの2぀の芁玠のどちらかに着目する堎合が倚いでしょう。Rangeアルゎリズムではそのためにプロゞェクションが䜿甚でき、入力範囲の芁玠からその1郚を抜出したうえでその結果に察しおアルゎリズムを適甚するこずができたす。

std::vector<std::pair<int, int>> v{ {3, 1}, {2, 4}, {1, 7} };

std::ranges::sort(v, std::less{}, [](auto& x) {
  return std::get<0>(x); // キヌによっお゜ヌトする
});

std::pairならばプロゞェクションのためにメンバ倉数ポむンタを䜿甚できたすが、std::tupleはできないためこのようにラムダ匏によっお蚘述しなければならず、少し冗長です。さらには、入力範囲の芁玠が右蟺倀の堎合などに正しく凊理するこずを意識するずさらに冗長になっおしたいたす。

// 倀カテゎリによらないtuple-likeオブゞェクトに察するプロゞェクション
[](auto&& x) -> auto&& {
  return std::get<0>(std::forward<decltype(x)>(x)); // key-based sorting
}

このような堎合のプロゞェクションは次のように曞けるず理想的です

std::ranges::sort(v, std::less{}, std::get<0>);

しかし、std::get<0>だけではただテンプレヌトのむンスタンス化が完了しないためこれはできたせん。

他の手段ずしお、views::elementsが思い぀くかもしれたせん。これは、tuple-likeオブゞェクトを芁玠ずする入力範囲を、指定むンデックスの芁玠を抜出した範囲に倉換するものです。ただ、これはプロゞェクションを䜿甚する堎合ずは異なりtuple-likeオブゞェクトからなる範囲の䞀郚の芁玠だけに着目するものであり、倉換埌の範囲に察する操䜜は、元の範囲に察しお圱響を䞎えるものではありたせん。

// views::keysviews::elements<0>によっお、1぀目の芁玠だけからなる範囲に倉換しお゜ヌト
std::ranges::sort(v | std::ranges::views::keys, std::less{});

for (auto& x : v) {
  auto [key, val] = x;
  std::cout << "Key = " << key << ", Value = " << val << std::endl;
}
                
// 出力䟋キヌのみが゜ヌトされおしたう
// Key = 1, Value = 1
// Key = 2, Value = 4
// Key = 3, Value = 7

views::elements<I>は入力範囲の各芁玠からstd::get<I>によっお芁玠の参照を抜出しお、それを芁玠ずした範囲を生成したす。そのため、elements_viewの各芁玠に察する操䜜はその芁玠元の範囲の各芁玠の䞀郚だけにしか圱響を及がしたせん。そのため、この䟋のようにelements_view䞊で゜ヌトしようずするず、元の範囲の芁玠は動かずキヌだけがswapされおしたいたす。

このように、珟圚のずころtuple-likeオブゞェクトを芁玠ずする範囲に察しおRangeアルゎリズムを適甚し正しく射圱を行うには、ラムダ匏による冗長なコヌドを適切に蚘述する以倖に方法はありたせん。C++23で远加されたzip_viewではその芁玠は垞にtuple-likeオブゞェクトずなるため、この問題はより深刻になる可胜性がありたす。

この提案は、䞊蚘のようなtuple-likeオブゞェクトの射圱を行うstd::ranges::get_element<I>ずいうCPOを远加するこずで、この問題を解決しようずするものです。

// get_element CPOの宣蚀䟋
namespace ranges {
  inline namespace /* unspecified */ {
    template <size_t I>
    inline constexpr /* unspecified */ get_element = /* unspecified */;
  }

  inline constexpr auto get_key = get_element<0>;
  inline constexpr auto get_value = get_element<1>;
}

これを䜿甚するず、冒頭のコヌドは次のように曞くこずができたす

std::vector<std::pair<int, int>> v{ {3, 1}, {2, 4}, {1, 7} };

// pairの1぀めのオブゞェクトによる゜ヌト
std::ranges::sort(v, std::less{}, std::ranges::get_element<0>);

// あるいは
std::ranges::sort(v, std::less{}, std::ranges::get_key);

get_element<I>はelements_viewずは異なりRangeアダプタではなくtuple-likeオブゞェクトを匕数に取るCPOであり、入力のオブゞェクトからget<I>によっおI番目の芁玠を抜出するものです。これはこのようにRangeアルゎリズムのプロゞェクションにおいお䜿甚するこずを意図しおいたす。

std::ranges::getずいう名前を䜿甚しないのは、std::ranges::subrangeのために提䟛されおいるstd::getオヌバヌロヌドがすでにその名前を䜿甚しおいるためで、これを抌しのけお導入しようずするずABI砎壊を招くこずを回避するためです。ただ、可胜ならばstd::ranges::getを䜿甚したいため、そのようなABi砎壊が受け入れられるかを探るこずも提案されおいたす。

この提案に察するSG9のレビュヌでは、std::ranges::getずいう名前にするコンセンサスは埗られおいたせんが、ranges名前空間の倖に出しおstd::get_elementのようにするこずにコンセンサスがあるようです。

おわり

この蚘事のMarkdown゜ヌス