文書の一覧
全部で55本あり、SG22(C/C++相互互換性に関する研究グループ)のCの提案を除くと48本になります。
- P0323R10 std::expected
- P0447R13 Introduction of std::colony to the standard library
- P1121R3 Hazard Pointers: Proposed Interface and Wording for Concurrency TS 2
- P1122R3 Proposed Wording for Concurrent Data Structures: Read-Copy-Update (RCU)
- P1132R7 out_ptr - a scalable output pointer abstraction
- P1169R1 static operator()
- P1401R5 Narrowing contextual conversions to bool
- P1664R3 reconstructible_range - a concept for putting ranges back together
- P1673R3 A free function linear algebra interface based on the BLAS
- P1706R3 Programming Language Vulnerabilities for Safety Critical C++
- P1708R4 Simple Statistical Functions
- P1847R4 Make declaration order layout mandated
- P1938R3 if consteval
- P1949R7 C++ Identifier Syntax using Unicode Standard Annex 31
- P1967R3 #embed - a simple, scannable preprocessor-based resource acquisition method
- P1989R2 Range constructor for std::string_view 2: Constrain Harder
- P2128R4 Multidimensional subscript operator
- P2128R5 Multidimensional subscript operator
- P2168R2 generator: A Synchronous Coroutine Generator Compatible With Ranges
- P2186R2 Removing Garbage Collection Support
- P2201R1 Mixed string literal concatenation
- P2223R2 Trimming whitespaces before line splicing
- P2251R1 Require span & basic_string_view to be Trivially Copyable
- P2255R1 A type trait to detect reference binding to temporary
- P2265R1 Renaming any_invocable
- P2273R1 Making std::unique_ptr constexpr
- P2283R1 constexpr for specialized memory algorithms
- P2295R1 Correct UTF-8 handling during phase 1 of translation
- P2295R2 Support for UTF-8 as a portable source file encoding
- P2295R3 Support for UTF-8 as a portable source file encoding
- P2321R1 zip
- P2322R2 ranges::fold
- P2325R2 Views should not be required to be default constructible
- P2327R0 De-deprecating volatile compound assignment
- P2337R0 Less constexpr for <cmath>
- P2342R0 For a Few Punctuators More
- P2345R0 Relaxing Requirements of Moved-From Objects
- P2348R0 Whitespaces Wording Revamp
- P2350R0 constexpr class
- P2352R0 SG16: Unicode meeting summaries 2020-12-09 through 2021-03-24
- P2353R0 Metaprograms and fragments are needed in comma-separated contexts
- P2355R0 Postfix fold expressions
- P2356R0 Implementing Factory builder on top of P2320
- P2358R0 Defining Contracts
- P2360R0 Extend init-statement to allow alias-declaration
- P2361R0 Unevaluated string literals
- P2362R0 Make obfuscating wide character literals ill-formed
- P2363R0 Extending associative containers with the remaining heterogeneous overloads
P0323R10 std::expected
エラーハンドリングを戻り値で行うための型、std::expected<T, E>
の提案。
std::expected<T, E>
のオブジェクトは型T
かE
のどちらかの値を保持しており、T
の値を期待される値(正常値)、E
の値をエラーとして扱うことでエラーハンドリングを行います。これは、RustではResult<T, E>
として重宝されています。
std::expected
を使うと、std::optional<T>
はstd::expected<T, std::nullopt_t>
のように書くことができ、std::optional
をエラー処理に用いる時に失われてしまっていたエラー詳細を同時に保持しておくことができるようになります。
std::expected
のインターフェースはstd::optional
をベースに、エラー値を保持している場合の操作を加えた形のものになります。
#include <expected> auto maybe_succeed() -> std::expected<int, std::string_view> { bool is_err = false; // 何か失敗しうる処理 // ... if (is_err) { return 46; // 正常値を返す } else { return {std::unexpect, "error!"}; // エラー値を返す } } int main() { auto exp = maybe_succeed(); // bool値に変換して結果をチェック if (exp or exp.has_value()) { // 正常値の取り出し int n = *exp; int m = exp.value(); // 未定義動作 exp.error(); } else { // エラー値の取り出し std::string_view s = exp.error(); // 未定義動作 *exp; // 例外を投げる exp.value(); } }
優れたエラーハンドリングメカニズムには、次のような事が求められます。
- エラーの可視性 : なんらかの処理が失敗するケースがコード上で隠されず、明確に表示されている
- エラーの情報 : エラーにはその発生場所や原因などが含まれる
- クリーンコード : エラー処理はできる限り目立たないように行われる。エラー処理は読みやすくなければならない
- 被侵入的 : エラーが通常の処理フローのためのチャネルを独占しない。それらはなるべく分離されているべき
この観点から、std::expected
と例外機構や通常のエラーコード戻り値によるエラー処理を比較すると
性質 | std::expected |
例外 | エラーコード戻り値 |
---|---|---|---|
可視性 | ◯ | △ | ◯ |
情報 | ◯ | ◯ | △ |
クリーンコード | ◯※ | ◯ | × |
非侵入的 | ◯ | ◯ | × |
※ monadicインターフェースがある場合(現在の提案には欠けている)
このように、std::expected
はエラー処理という観点から既存の方法よりも優れています。また、例外機構と比べるとパフォーマンスでも勝ります。
- エラーハンドリングを綺麗にこなすためのライブラリ・expectedの紹介と応用 - Qita
- エラー値と正常値を表す汎用的な型:expected - Faith and Brave - C++で遊ぼう
- P0323 進行状況
この部分の6割は以下の型のご指摘によって構成されています。
P0447R13 Introduction of std::colony to the standard library
要素が削除されない限りそのメモリ位置が安定なコンテナであるstd::colony
の提案。
以前の記事を参照
- P0447R11 Introduction of std::colony to the standard library - [C++]WG21月次提案文書を眺める(2020年12月)
- P0447R12 Introduction of std::colony to the standard library - [C++]WG21月次提案文書を眺める(2021年01月)
このリビジョンでの変更は、実装を過度に指定しないように文言を修正したことと、一部の非メンバ関数をfriend
関数に変更したことなどです。
P1121R3 Hazard Pointers: Proposed Interface and Wording for Concurrency TS 2
標準ライブラリにハザードポインタを導入する提案。
以前の記事を参照
このリビジョンでの変更は、一般的な設計に関する情報と例の追記、用語の変更・追加・整理、一部のデフォルトコンストラクタを非explicit
にし、一部のコンストラクタにnoexecept
を追加したことなどです。
この提案はLWGのレビューを完了しており、このリビジョンをもってConcurency TS v2導入のための全体投票にかけられる事が決まっています。何事もなければ、次の全体会議にてConcurency TSに入ることになります。
P1122R3 Proposed Wording for Concurrent Data Structures: Read-Copy-Update (RCU)
標準ライブラリにRead-Copy-Update(RCU)を導入する提案。
RCUは並行処理におけるデータ共有のための仕組みで、ロックフリーデータ構造の実装に用いることができます。
RCUでは、共有対象のデータはアトミックなポインタによって共有されており、共有データを更新する際は別の領域にデータを構築してから、ポインタを差し替えることで行います(ここまではハザードポインタと同じ)。共有データに触る際のクリティカルセクションでは、スケジューリング(プリエンプション)が起こらないようにして、クリティカルセクションはあるスレッドを占有して実行されるようにしてから、共有データへアクセスします。クリティカルセクションに入る際はOSの機能を使用してそのスレッドがスケジューリングされないようにしますが、何かロックを取得したりするわけではないのでオーバーヘッドはほぼゼロです。
読み取りの際は、共有データを読みだしている間そのスレッドはスケジューリングされず、データを見終わった後で通常通りスケジューリング対象に戻ります。
更新の際は、更新後のデータを別の領域に用意してからクリティカルセクションに入り、共有データを指すポインタを新しいデータを指すように差し替えます(この差し替えはアトミック)。その後、他のスレッドが最低一回以上スケジューリングされるまで待機してから、差し替え前の古いデータを削除しクリティカルセクションを抜けます。
クリティカルセクション中そのスレッドはスケジューリングされないので、処理はOSによって中断される事なく実行されています。そのため、更新スレッドから見て他のスレッドに対してスケジューリング一回以上行われたということは、共有データを読み取っている(更新前の古いデータを見ている)可能性のあるスレッドが読み取り処理を終えている事を意味します。
スレッドがスケジューリングされないようになるということは、クリティカルセクションの実行はCPUの論理コアを占有することになります。従って、更新スレッドが実行されているコアを除いたシステムのCPUの残りの論理コアにおいてスレッドの切り替えが一回以上行われていれば、更新スレッドが保持している古いデータを見ているスレッド(=クリティカルセクション)が無いこと分かるため、安全に削除できるということです。
データの差し替えはポインタによってアトミックに行われるため、更新が同時に行われてもデータ競合を起こしませんし、デッドロックすることもありません。ただし、RCUの全ての保証はユーザーが正しくクリティカルセクションを運用する事を前提としています。
RCUを使用して、複数のリーダー(reader)が並行して存在する時に、共有データの更新を実行する例。
// 共有したいもの std::atomic<std::string *> name; // 並行して頻繁に呼ばれる void print_name() { // RAIIによって読み取りクリティカルセクションを構成 std::rcu_reader rr; std::string *s = name.load(std::memory_order_acquire); /* ...use *s... */ } // あまり呼ばれない void update_name(std::string_view &nn) { // 1. 値の更新(新しい値の用意と共有ポインタのアトミックな差し替え) std::string *new_name = new std::string(nn); std::string *s = name.exchange(new_name, std::memory_order_acq_rel); // 2. 遅延削除(更新クリティカルセクション) std::rcu_retire(s); }
リーダー(print_name()
)は1つのスレッドで実行され、更新処理(update_name()
)はそれとは別のスレッドで実行されます。print_name()
内rcu_reader
のオブジェクトは、リーダーの処理が完了するまで参照しているオブジェクトが、rcu_retire()
によって削除されないように保護しています。
RCUは読み取りが頻繁に起こるが更新はあまりされないような場合に適した方法です。
この提案は別の提案(P0461R2)の標準へ提案する文書をまとめたもので、Concurrency TSに向けて提案され、議論されています。
著者の方(Paul McKenneyさん)の実装経験をベースにして長い時間をかけて議論されてきており、Concurrency TSに向けての議論がLWGまで進行しているのでConcurrency TSには入りそうですが、標準ライブラリの一部として利用可能となるにはもう少しかかりそうです。
- P0461R2 Proposed RCU C++ API
- P1121R2 Hazard Pointers: Proposed Interface and Wording for Concurrency TS 2 - WG21月次提案文書を眺める(2021年01月)
- P0561R5 An RAII Interface for Deferred Reclamation - WG21月次提案文書を眺める(2020年12月)
- HTMはメモリ管理の為に生まれてきたんだよ! ΩΩ<な、なんだってー - Software Transactional Memo
- P1122 進行状況
P1132R7 out_ptr - a scalable output pointer abstraction
スマートポインタとポインタのポインタを取るタイプのC APIとの橋渡しを行う、std::out_ptr
の提案。
C APIでは、関数の中でのメモリアロケーションの結果などを呼び出し元に出力するために、ポインタのポインタ(T** / void**
)を引数に取るものがよく見られます。ポインタではなくポインタのアドレスを渡す必要があり、std::unique_ptr
をはじめとするスマートポインタはそれを取得する手段がないため、相性が良くありませんでした。
提案文書より、ffmpegにおけるサンプル。
#include <memory> #include <avformat.h> // カスタムデリータ struct AVFormatContextDeleter { void operator() (AVFormatContext* c) const noexcept { avformat_close_input(&c); avformat_free_context(c); } }; using av_format_context_ptr = std::unique_ptr<AVFormatContext, AVFormatContextDeleter>; // libavformatのシグネチャ //int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options); int main (int, char* argv[]) { av_format_context_ptr context(avformat_alloc_context()); // ... // used, need to reopen // AVFormatContext**にunique_ptrをセットしたいが・・・ AVFormatContext* raw_context = context.release(); if (avformat_open_input(&raw_context, argv[0], nullptr, nullptr) != 0) { // 処理が失敗した場合にデリータが走って欲しくないので、一旦release()している std::stringstream ss; ss << "ffmpeg_image_loader could not open file '" << path << "'"; throw FFmpegInputException(ss.str().c_str()); } // 成功したら出力されたポインタをセット context.reset(raw_context); // ... off to the races ! return 0; }
std::out_ptr/std::inout_ptr
はこのような場合のrelease()
とreset()
の呼び出しを自動化し、スマートポインタに内包されているポインタのアドレスを抽出するものです。
int main (int, char* argv[]) { av_format_context_ptr context(avformat_alloc_context()); // ... // used, need to reopen // release()してからポインタのアドレスを渡し、終了後に元のunique_ptrにreset()する if (avformat_open_input(std::inout_ptr(context), argv[0], nullptr, nullptr) != 0) { std::stringstream ss; ss << "ffmpeg_image_loader could not open file '" << argv[0] << "'"; throw FFmpegInputException(ss.str().c_str()); } // ... off to the races! return 0; }
このようなユーティリティは、大小様々な企業において再発明されていますが、その用途や目的はC APIの出力ポインタ(T**
)引数にスマートポインタを直接渡せるようにする事で一貫しています。この提案は、そのような目的が同じでありながらそれぞれで再実装されているユーティリティの、将来性があり高性能で使いやすい共通の実装を標準で提供する事を目指したものです。
std::out_ptr/std::inout_ptr
は関数であり、引数で受け取ったスマートポインタをラップした型(std::out_ptr_t/std::inout_ptr_t
)のオブジェクトを返します。それらの型は元のポインタ型やvoid**
への暗黙変換演算子を備えており、コンストラクタとデストラクタでrelease()
とreset()
を行います。
std::out_ptr
とstd::inout_ptr
の違いは、対象となるスマートポインタが予めリソースの所有権を持っているか否かによって使い分けるためにあります。
error_num c_api_create_handle(int seed_value, int** p_handle); error_num c_api_re_create_handle(int seed_value, int** p_handle); void c_api_delete_handle(int* handle); struct resource_deleter { void operator()( int* handle ) { c_api_delete_handle(handle); } }; // out_ptrの使用例 void normal_case() { std::unique_ptr<int, resource_deleter> resource(nullptr); // スマートポインタからそのポインタのアドレスへの変換をラップする error_num err = c_api_create_handle(24, std::out_ptr(resource)); if (err == C_API_ERROR_CONDITION) { // handle errors } } // inout_ptrの使用例 void reallocate_case(std::unique_ptr<int, resource_deleter> resource) { // 予めリソースの所有権を保持している場合 // release()してからポインタのアドレスを渡し、終了後に元のunique_ptrにreset()する error_num err = c_api_re_create_handle(24, std::inout_ptr(resource)); if (err == C_API_ERROR_CONDITION) { // handle errors } // resource.get() the out-value from the C API function }
std::out_ptr
は空のスマートポインタにC API経由でリソースをセットするときに使用し、std::inout_ptr
は予めリソースの所有権を保持しているスマートポインタをC APIに渡すときに使用します。
その他のサンプル
#include <memory> #include <cstdio> // C API関数 int fopen_s(std::FILE** f, const char* name, const char* mode); // カスタムデリータ struct fclose_deleter { void operator()(std::FILE* f) const noexcept { std::fclose(f); } }; int main(int, char*[]) { constexpr const char* file_name = "ow.o"; std::unique_ptr<std::FILE, fclose_deleter> file_ptr; // unique_ptrをFILE**へ渡すためのキャスト構文 int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr), file_name, "r+b"); if (err != 0) { return 1; } // *file_ptr is valid return 0; }
#include <memory> struct StarFish* starfish_alloc(); // C API関数 int starfish_populate(struct StarFish** ps, const char *description); // カスタムデリータ struct StarFishDeleter { void operator() (struct StarFish* c) const noexcept; }; using StarFishPtr = std::unique_ptr<StarFish, StarFishDeleter>; int main(int, char*[]) { StarFishPtr peach(starfish_context()); // ... // used, need to re-make int err = starfish_populate(std::inout_ptr(peach), "caring clown-fish liker"); return err; }
この提案は元々C++20入りを目指していましたが間に合いませんでした。C++20作業終了時点で提案は既にLWGに送付済みで、コロナウィルス流行などによって遅れていましたが、既にLWGでの最後のレビューが完了しています。次の全体会議で投票にかけられ、何事もなければC++23に導入されます。
この部分の6割は以下の方々のご指摘によって構成されています。
P1169R1 static operator()
関数呼び出し演算子(operator()
)を、静的メンバ関数として定義できるようにする提案。
任意の関数オブジェクトを取ることで処理をカスタマイズできるようにすることは、標準ライブラリの多くの所で行われています。関数呼び出し演算子をテンプレートにすることで、オーバーロードを用意するよりもより簡易かつ便利に関数オブジェクトを利用できます。
また、C++20からはカスタマイゼーションポイントオブジェクト(CPO)と呼ばれる関数オブジェクトが多数追加されています。これらのCPOはメンバを持たないことが規定されています。
ラムダ式にせよ、ユーザー定義のものにせよ、CPOにせよ、関数呼び出し演算子のオーバーロードによって関数オブジェクトは成り立っていますが、関数呼び出し演算子は非静的メンバ関数としてしか定義できません。従って、その呼び出しに当たっては暗黙のthis
引数が渡されています。
CPOやキャプチャしていないラムダのようにメンバアクセスの必要が皆無だったとしても、関数呼び出しがインライン展開されなければ関数呼び出し演算子オーバーロードによる呼び出しは常にthis
ポインタを引き渡すオーバーヘッドを埋め込んでいます。
struct X { bool operator()(int) const; static bool f(int); }; // 簡易的なCPO定義 inline constexpr X x; int count_x(std::vector<int> const& xs) { return std::count_if(xs.begin(), xs.end(), #ifdef STATIC X::f // 静的メンバ関数を渡す #else x // 関数オブジェクト(CPO)を渡す #endif ); }
決して使用されない事が分かっていても、コンパイラは必ずしもこのオーバーヘッドを取り除くことができません。これはゼロオーバーヘッド原則に違反していますが、関数呼び出し演算子を静的メンバ関数として定義することはできません。
この提案は、この様なオーバーヘッドを取り除くために、関数呼び出し演算子を静的メンバ関数として定義できるようにしようとするものです。ステートレスな関数オブジェクトの有用性は、C++11のラムダ式導入以降広く認識されており、この様な制限を課しておく事に利益はありません。
そのほかのオーバーロード可能な演算子にも同様の事が言えますが、関数呼び出し演算子以外のものを静的メンバ関数として定義できるようにするユースケースが見られないため、ここでは関数呼び出し演算子だけを対象としています。
この提案の後では、キャプチャをしていないラムダ式の関数呼び出し演算子をstatic
に定義することができるようになりますが、それをしてしまうとラムダ式の関数呼び出し演算子のメンバポインタを取り出すようなコードが壊れ、またABI破損に繋がります。
そのため、キャプチャしていないラムダ式の関数呼び出し演算子をstatic
になるようにしてしまうことは出来ないため、オプトインする構文を提案しています。
// 関数呼び出し演算子が静的メンバ関数として定義される auto four = []() static { return 4; }; // 従来通り、非静的メンバ関数として定義される auto five = []() { return 5; };
P1401R5 Narrowing contextual conversions to bool
constexpr if
やstatic_assert
の引数でのみ、整数型からbool
型への暗黙の縮小変換を定数式で許可する提案。
以前の記事を参照
- P1401R3 Narrowing contextual conversions to bool - [C++]WG21月次提案文書を眺める(2020年06月)
- P1401R4 Narrowing contextual conversions to bool - [C++]WG21月次提案文書を眺める(2020年10月)
このリビジョンでの変更は、CWGからの指摘を受けて提案する文言を調整した事です。
この提案はEWGのレビューを通過し、CWGに転送されています。
P1664R3 reconstructible_range - a concept for putting ranges back together
view
によって別のrange
に変換されてしまった範囲を、元のrange
(と同じ型)に戻す操作、std::ranges::reconstruct
と関連するコンセプトの提案。
Range Adopterの適用では、入力のrange
はview
の型に包まれる形で変換され、元の型とは別の型になってしまい、元には戻らなくなります。
template <typename T> using span = quickcpplib::span<T>; std::vector<int> vec{1, 2, 3, 4, 5}; span<int> s{vec.data(), 5}; // 現在、コンパイルエラー span<int> v = s | views::drop(1) | views::take(10) | views::drop(1) | views::take(10); // これならOK auto v2 = s | views::drop(1) | views::take(10) | views::drop(1) | views::take(10); // decltype(v2) == ranges::take_view<ranges::drop_view<ranges::take_view<ranges::drop_view<span<int, dynamic_extent>>>>>
この例では、decltype(v)
からspan<int>
への変換方法がないためコンパイルエラーを起こしています。
views::take/views::drop
は指定された数だけイテレータを保存するor落とすことで、指定された数だけ要素を取り出すor無視する、という処理を行っており、view
の適用後も元のイテレータをそのまま利用しています。そのため、元のイテレータを用いれば元の型を再構成できるはずです。しかし、現在はその方法や判別方法がありません。
この提案は、そのような場合に再構成できるrange
を表すReconstructible Rangesという概念を導入し、そのためのADL-foundな関数とコンセプトを整備するものです。
先程の例の場合のようにイテレータとセンチネルのペアから元の型を再構成できる場合、それを行うためのカスタマイゼーションポイントとしてreconstruct
関数を利用します。
それをstd::ranges::reconstruct
CPOによって呼び出すようにし、reconstruct
CPOによってpair_reconstructible_range
とreconstructible_range
の二つのコンセプトを定義します。
namespace std::ranges { inline namespace unspecified { inline constexpr nspecified reconstruct = unspecified; } template <class R, class It = ranges::iterator_t<remove_reference_t<R>>, class Sen = ranges::sentinel_t<remove_reference_t<R>>> concept pair_reconstructible_range = ranges::range<R> && ranges::borrowed_range<remove_reference_t<R>> && requires (It first, Sen last) { reconstruct( in_place_type<remove_cvref_t<R>>, std::move(first), std::move(last) ); }; template <class R, class Range = remove_reference_t<R>> concept reconstructible_range = ranges::range<R> && ranges::borrowed_range<remove_reference_t<R>> && requires (Range first_last) { reconstruct( in_place_type<remove_cvref_t<R>>, std::move(first_last) ); }; }
std::ranges::reconstruct
CPOはタグ型(in_place_type<R>
)とR
のイテレータペア、もしくはタグ型とrange
を受け取り、そのイテレータ型について呼び出し可能なreconstruct
関数を呼び出し、処理を委譲します。
reconstruct
関数では、それぞれのイテレータ(range
)に最適な方法によってイテレータペアから元のrange
の再構成を行います。
そして、標準ライブラリのviews::take/views::drop
の呼び出しは、reconstructible_range
コンセプトのモデルとなる型に対して、元のイテレータを使用して直接元の型を再構成して結果を返すようにします。
これによって冒頭のコードは次のようになります
template <typename T> using span = quickcpplib::span<T>; std::vector<int> vec{1, 2, 3, 4, 5}; span<int> s{vec.data(), 5}; // OK auto v = s | views::drop(1) | views::take(10) | views::drop(1) | views::take(10); // decltype(v2) == span<int>
他の例
std::u8string name = "𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑 𐑡𐑹𐑡 ·𐑚𐑻𐑯𐑸𐑛 ·𐑖𐑷"; char16_t conversion_buffer[432]; std::u8string_view name_view(name); std::span<char16_t> output(conversion_buffer, 432); auto encoding_result = ztd::text::transcode(input, output); // 全てOK auto unprocessed_code_units = encoding_result.input; auto unconsumed_output = encoding_result.output; // decltype(unprocessed_code_units) == std::u8string_view // decltype(unconsumed_output) == std::span<char16_t>
P1673R3 A free function linear algebra interface based on the BLAS
標準ライブラリに、BLASをベースとした密行列のための線形代数ライブラリを追加する提案。
この提案は、BLASのAPIをベースとした密行列のための各種操作を行うフリー関数を追加する提案もので、ベクトル型や行列型を追加するものではなく、これらの関数は特定のデータ構造に依存していません。
関数はテンプレートで定義され、提案中のmdspan
を用いることで特定のデータ型に依存しないようにされています。また、演算子オーバーロードを用いたexpressionテンプレートなインターフェースでもありません。
線形代数ライブラリ(ベクトル/行列型)を追加する提案は別に進行しておりユーザーが触れるインターフェースとなるのはそちらで、これは線形代数ライブラリの基礎となるものです。
提案文書より、コレスキー分解のサンプル
#include <linalg> #include <cmath> template<class inout_matrix_t, class Triangle> int cholesky_factor(inout_matrix_t A, Triangle t) { using element_type = typename inout_matrix_t::element_type; constexpr element_type ZERO {}; constexpr element_type ONE (1.0); const ptrdiff_t n = A.extent(0); if (n == 0) { return 0; } else if (n == 1) { if (A(0,0) <= ZERO || std::isnan(A(0,0))) { return 1; } A(0,0) = std::sqrt(A(0,0)); } else { // Partition A into [A11, A12, A21, A22], // where A21 is the transpose of A12. const std::ptrdiff_t n1 = n / 2; const std::ptrdiff_t n2 = n - n1; auto A11 = std::subspan(A, std::pair{0, n1}, std::pair{0, n1}); auto A22 = std::subspan(A, std::pair{n1, n}, std::pair{n1, n}); // Factor A11 const int info1 = cholesky_factor(A11, t); if (info1 != 0) { return info1; } using std::linalg::symmetric_matrix_rank_k_update; using std::linalg::transposed; if constexpr (std::is_same_v<Triangle, upper_triangle_t>) { // Update and scale A12 auto A12 = subspan(A, std::pair{0, n1}, std::pair{n1, n}); using std::linalg::triangular_matrix_matrix_left_solve; triangular_matrix_matrix_left_solve(transposed(A11),upper_triangle, explicit_diagonal, A12); // A22 = A22 - A12^T * A12 symmetric_matrix_rank_k_update(-ONE, transposed(A12), A22, t); } else { // // Compute the Cholesky factorization A = L * L^T // // Update and scale A21 auto A21 = std::subspan(A, std::pair{n1, n}, std::pair{0, n1}); using std::linalg::triangular_matrix_matrix_right_solve; triangular_matrix_matrix_right_solve(transposed(A11), lower_triangle, explicit_diagonal, A21); // A22 = A22 - A21 * A21^T symmetric_matrix_rank_k_update(-ONE, A21, A22, t); } // Factor A22 const int info2 = cholesky_factor(A22, t); if (info2 != 0) { return info2 + n1; } } }
なお、Wordingのページだけで120P近くを占めており、とてつもなく巨大です・・・
- P1417R0: Historical lessons for C++ linear algebra library
- BLAS - NEC Numeric Library Collection 2.3.0 ユーザーズガイド
- P1673 進行状況
P1706R3 Programming Language Vulnerabilities for Safety Critical C++
MISRA C++およびWG23の文書についてのWG21 SG12でのレビュー作業の進捗状況に関する文書。
WG23は様々なプログラミング言語における脆弱性を調査するISOのワーキンググループです。2017年以降、WG23とWG21はC++の脆弱性を文書化するための作業を共同で行っています。その後、途中でMISRA C++の関係者もレビュープロセスに加わりました。
この作業の目的は単に脆弱性がどのようなコードから発生するのかを文書化することにあります。
P1708R4 Simple Statistical Functions
標準ライブラリにいくつかの統計関数を追加する提案。
以前の記事を参照
このリビジョンでの変更は、NumPyを参考に一部の関数仕様を調整したことと、stats_error
を定数からクラスへ変更した事です。
この提案はSG16でのレビューを通過し、LEWGに転送されています。どうやらNumeric TSへ導入することを目指して議論されています。
P1847R4 Make declaration order layout mandated
クラスのデータメンバのメモリレイアウトを、宣言順に一致するように規定する提案。
現在の規定では、アクセス制御(private, public, protected
)が異なる場合、実装はデータメンバを並べ替えてメモリに配置することができます。ただ、実際にそれを行う処理系は存在せず、実際のプログラムでは並べ替えを考慮されていないことがほとんどです。
この提案は、そのような慣行に従うように規定を修正し、クラスのデータメンバのメモリレイアウトが常にコード上の宣言順と一致するようにするものです。それによって、規則が単純になり、将来クラスレイアウトをコントロールするための機能を追加する際の土台とすることができます。
クラスレイアウトに関するこの制限は当初のC++から存在していたものではなく、C++11から偶発的に混入したもののようです。
当初のC++には、POD構造体はC言語との互換性がある必要がありましたがそれ以外のところに制限は特にありませんでした。そこで、クラスレイアウトをコントロールしたいユーザーから、ブロックやラベルによってクラスのデータメンバの配置をコントローする構文が提案されていました。これはC++11以前のことです。
C++11でその提案は採択されず、アクセス制御のみによってデータメンバの配置が変更されうるようにされました(N2342)。これは標準レイアウトクラスに関する作業で、クラスのレイアウトが不定になるような制限を導入する意図はなかったようです。
N2342による変更はC++が標準化されてから10年以上経過した後になされたもので、潜在的にはABIを破損する可能性がありましたが、これを活用する実装は現れなかったため実際に問題になることはありませんでした。
N2342による変更は最適ではなく、この点からもこのことは修正すべきです。ABI破損の可能性は悩ましいですが、目的を持って制御できなければ利点はありません。
P1938R3 if consteval
consteval
とstd::is_constant_evaluated()
にある分かりづらい問題点を解決するためのconsteval ifステートメントの提案
以前の記事を参照
このリビジョンでの変更は、提案する文言の調整です。
この提案は、CWGとLWGでのレビューを終えており、次の会議で全体投票にかけられることが決まっています。何事もなければC++23に入りそうです。
P1949R7 C++ Identifier Syntax using Unicode Standard Annex 31
識別子(identifier)の構文において、不可視のゼロ幅文字や制御文字の使用を禁止する提案。
以前の記事を参照
- P1949R4 C++ Identifier Syntax using Unicode Standard Annex 31 - WG21月次提案文書を眺める(2020年4月)
- P1949R5 C++ Identifier Syntax using Unicode Standard Annex 31 - WG21月次提案文書を眺める(2020年6月)
- P1949R6 C++ Identifier Syntax using Unicode Standard Annex 31 - WG21月次提案文書を眺める(2020年8月)
このリビジョンの変更点は、よくわかりません。
この提案はCWGのレビューを終え、次の全体投票にかけられることが決まっています。何事もなければC++23に入りそうです。
P1967R3 #embed - a simple, scannable preprocessor-based resource acquisition method
コンパイル時(プリプロセス時)にバイナリデータをインクルードするためのプリプロセッシングディレクティブ#embed
の提案。
ハードリセット時にハードをフラッシュするためのベースイメージ、アイコンファイル、プログラムと強く連携するスクリプトなど、アプリケーションにバイナリデータを同梱したい場合があります。しかし、C/C++においてそれを行う簡易な方法はありません。
xxd
コマンドを利用して16進リテラルとして埋め込んだり、環境のリンカを直接叩いてデータを埋め込み、それを指すextern
ポインタを利用するなど、様々な方法が考案されてきましたが、それを移植可能にしたり、ビルドシステムで管理しようとしたりすることは非常に困難です。
この提案は#embed
というプリプロセッシングディレクティブを導入することで、簡易かつ移植可能な方法でバイナリデータをプログラムに埋め込めるようにしようとするものです。
新しいプリプロセッシングディレクティブという言語機能としてサポートしようとしているのは、数値リテラルの配列としてバイナリデータを埋め込む方法が非効率極まりないためです。
#embed
は#include
に従うように設計されており、ほぼ同様に使用できます。
// デフォルトはunsigned char const unsigned char icon_display_data[] = { #embed "art.png" }; // 整数定数式で初期化可能な任意の型を指定できる const char reset_blob[] = { #embed "data.bin" };
ただしこれは、あたかもバイナリデータを整数値としてコンマ区切りリストで展開しているかのように動作するというだけで、実際にそのような展開が起きているわけではありません。それをするとコンパイル時間を著しく増大させるためです。
すなわち、#embed
によるバイナリデータ読み取りと展開はコピペではなく、コンパイラによって最適な方法で処理されています。
また、無限あるいは巨大なファイルの一部だけを読むために、長さを指定できるようになっています。
// 最大で32バイトの乱数を読み取る const int please_dont_oom_kill_me[] = { #embed 32 "/dev/urandom" };
ただし、この指定は上限であり厳密な要求値ではありません。実際に読み取った長さはより小さくなる可能性があります(その場合でも、配列長でコンパイル時に判定可能)。
P1989R2 Range constructor for std::string_view 2: Constrain Harder
std::string_view
のコンストラクタにrangeオブジェクトから構築するコンストラクタを追加する提案。
以前の記事を参照
このリビジョンでの変更は、LWGのレビューを受けて文言を修正したことです。
この提案はLWGのレビューを終えており、次の全体会議で投票にかけられることが決まっています。これもC++23に入りそうです。
P2128R4 Multidimensional subscript operator
↓
P2128R5 Multidimensional subscript operator
多次元コンテナサポートのために添字演算子([]
)が複数の引数を取れるようにする提案。
前回の記事を参照
- P2128R1 Multidimensional subscript operator - [C++]WG21月次提案文書を眺める(2020年5月)
- P2128R2 Multidimensional subscript operator - [C++]WG21月次提案文書を眺める(2020年7月)
- P2128R3 Multidimensional subscript operator - [C++]WG21月次提案文書を眺める(2021年2月)
R4での変更は、動機付けや代替案についての議論をより追記した事と、機能テストマクロを追加した事などです。
このリビジョンでの変更は、EWGの指示に基づいて、動機付けやユースケースについて追記した事などです。
この提案はEWGでのレビューが終了し、CWGに転送するための投票にかけられる予定です。
P2168R2 generator: A Synchronous Coroutine Generator Compatible With Ranges
Rangeライブラリと連携可能なT
型の要素列を生成するコルーチンジェネレータstd::generator<T>
の提案。
前回の記事を参照
- P2168R0 generator: A Synchronous Coroutine Generator Compatible With Ranges - WG21月次提案文書を眺める(2020年5月)
- P2168R2 generator: A Synchronous Coroutine Generator Compatible With Ranges - WG21月次提案文書を眺める(2021年01月)
このリビジョンでの変更は、文言の修正、アロケータサポートの解説の改善、実装例の更新などです。
P2186R2 Removing Garbage Collection Support
ガベージコレクタサポートのために追加された言語とライブラリ機能を削除する提案。
以前の記事を参照
- P2186R0 Removing Garbage Collection Support - WG21月次提案文書を眺める(2020年7月)
- P2186R1 Removing Garbage Collection Support - WG21月次提案文書を眺める(2021年1月)
このリビジョンでの変更は、削除するライブラリ名の名前を専用のセクションに追記した事です。
この提案はCWGとLWGのレビューを終えており、次の全体会議で投票にかけられる事が決まっています。何事もなければ、C++23に適用されます。
P2201R1 Mixed string literal concatenation
異なるエンコードプレフィックスを持つ文字列リテラルの連結を禁止する提案。
このリビジョンでの変更は、この変更を規格書のAnnex Cセクションに追記されるように文言を追加した事です。
この提案はすでにCWGのレビューを終え、次の全体会議で投票にかけられる事が決まっています。
また、これと同等の内容がすでにC言語には適用されているようです(N2594)。
P2223R2 Trimming whitespaces before line splicing
バックスラッシュ+改行による行継続構文において、バックスラッシュと改行との間にホワイトスペースの存在を認める提案。
以前の記事を参照
- P2223R0 Trimming whitespaces before line splicing - WG21月次提案文書を眺める(2020年9月)
- P2223R1 Trimming whitespaces before line splicing - WG21月次提案文書を眺める(2020年10月)
このリビジョンでの変更は、CWGのレビューを受けて提案する文言を修正した事です。
この提案はすでにCWGのレビューを終え、次の全体会議で投票にかけられる事が決まっています。
P2251R1 Require span & basic_string_view to be Trivially Copyable
std::span
とstd::string_view
はtrivially copyableである、と規定する提案。
以前の記事を参照
このリビジョンでの変更は、動機付けを追記した事、LEWGでの投票結果を記載した事、3つの主要実装がすでにそうなっている事を確認できるCompiler Explorerへのリンクを追記した事などです。
この提案は非常に小さいため、LEWGのレビューを簡易にパスして、LWGに送るためのLEWGでの投票待ちをしています。
P2255R1 A type trait to detect reference binding to temporary
一時オブジェクトが参照に束縛されたことを検出する型特性を追加し、それを用いて一部の標準ライブラリの構築時の要件を変更する提案。
以前の記事を参照
このリビジョンでの変更は、この提案によって影響を受けるtuple/pair
のコンストラクタをオーバーロード解決から除外するのではなくdelete
として定義するようにした事です。
この提案はEWGでのレビューをパスしてLEWGのレビュー待ちをしています。
P2265R1 Renaming any_invocable
提案中のany_invocable
の名前を変更する提案。
以前の記事を参照
このリビジョンでの変更はよくわかりません。この提案はすでにP0288に適用されています。
P2273R1 Making std::unique_ptr constexpr
std::unique_ptr
を全面的にconstexpr
対応する提案。
以前の記事を参照
このリビジョンでの変更は、機能テストマクロを__cpp_lib_constexpr_memory
をバージョンアップするように変更したことと、nullptr
に対してもconstexpr
でswap
、比較とdefault_deleter
が機能するようにしたことです。
P2283R1 constexpr for specialized memory algorithms
<memory>
にある未初期化領域に対する操作を行う各関数をconstexpr
にする提案。
以前の記事を参照
このリビジョンでの変更は、機能テストマクロを追加したこと、文言の影響範囲を明確にしたこと、default_construct_at
の必要性の説明を追記したことです。
P2295R1 Correct UTF-8 handling during phase 1 of translation
↓
P2295R2 Support for UTF-8 as a portable source file encoding
↓
P2295R3 Support for UTF-8 as a portable source file encoding
C++コンパイラが少なくともUTF-8をサポートするようにする提案。
以前の記事を参照
R1での変更は、ホワイトスペースについてのセクションと関連する文言を取り除いたことです。どうやら、空白文字を具体的に指定することを避けたようです(SG16では合意自体は取れていたようですが)。
R2での変更は、BOMについてのガイドラインを追記したこと、Clangが将来的に幅広いエンコーディングを採用する予定であることを明確にしたこと、翻訳フェーズ5時点でのコードポイントの保存がP22314R1によって処理されることを明確にしたことなどです。
R3での変更は、SG16のガイダンスに従って、文言を修正したことです。
P2321R1 zip
<ranges>
にzip_view, adjacent_view, zip_transform_view, adjacent_transform_view
を追加する提案。
以前の記事を参照
このリビジョンでの変更は機能テストマクロを追加したこと、前方向またはそれよりも弱いzip_view
のイテレータのoperator==
についての説明を追記したこと、adjacent_view
がinput_range
をサポートしない事についての説明、簡単なサンプルコードの追加および文言の修正です。
std::vector v1 = {1, 2}; std::vector v2 = {'a', 'b', 'c'}; std::vector v3 = {3, 4, 5}; fmt::print("{}\n", std::views::zip(v1, v2)); // {(1, 'a'), (2, 'b')} fmt::print("{}\n", std::views::zip_transform(std::multiplies(), v1, v3)); // {3, 8} fmt::print("{}\n", v2 | std::views::pairwise); // {('a', 'b'), ('b', 'c')} fmt::print("{}\n", v3 | std::views::pairwise_transform(std::plus())); // {7, 9}
P2322R2 ranges::fold
range
アルゴリズムであるranges::fold
の提案。
以前の記事を参照
- P2322R0 ranges::fold - [C++]WG21月次提案文書を眺める(2021年02月)
- P2322R1 ranges::fold - [C++]WG21月次提案文書を眺める(2021年03月)
このリビジョンでの変更は、weakly-assignable-from
コンセプトを使用していたところをassignable_from
コンセプトに置き換えた事、fold
の戻り値型は初期値の型ではなくなった事です。
fold
の戻り値型の問題は次のようなコードで結果がどうなるのかという事です。
namespace std::ranges { template <range R, movable T, typename F, typename U = /* ... */> auto fold(R&& r, T init, F f) -> U { // ... } } std::vector<double> v = {0.25, 0.75}; auto r = ranges::fold(v, 1, std::plus()); // r == 2.0 ? r == 1 ??
この場合に、ranges::fold
の戻り値型をU = std::decay_t<std::invoke_result_t<F&, T, ranges::range_reference_t<R>>
の様に決めることで戻り値型が初期値から決まらないようにしています。それに伴って必要な制約を追加して、この提案では上記のr
はdouble
の2.0
になります。
P2325R2 Views should not be required to be default constructible
Viewとみなされる型にデフォルト構築可能性を要求しない様にする提案。
以前の記事を参照
- P2325R0 Views should not be required to be default constructible - [C++]WG21月次提案文書を眺める(2021年02月)
- P2325R1 Views should not be required to be default constructible - [C++]WG21月次提案文書を眺める(2021年03月)
このリビジョンでの変更は、標準に提案する文言を追加した事です。
この提案はまだLEWGでのレビュー中ですが、採択された際にC++20にさかのぼって適用される可能性があります。
P2327R0 De-deprecating volatile compound assignment
非推奨となったvolatile
値に対する複合代入演算子を再考する提案。
C++はOSの無い組み込みのプログラミングにおいても利用されています。そこではメモリにマップされたレジスタ(メモリマップドI/O)を操作することによってハードウェアを操作し、多くの場合特定の1bitにしか触りません。そこでは次のようなコードが頻出します。
// ベンダ提供の抽象化レイヤ struct ADC { volatile uint8_t CTRL; volatile uint8_t VALUE; ... }; #define ADC_CTRL_ENABLE ( 1 << 3 ) // in u se r code ADC1−>CTRL |= ADC_CTRL_ENABLE; // ADCのスタート ADC1−>CTRL &= ~ADC_CTRL_ENABLE; // ADCをストップ
このようなコードは、ベンダから提供されたマクロや関数の中で使用される場合もあるほか、コードジェネレーターが生成するコードに含まれていることもあります。
しかしこのようなvolatile
値に対する複合代入演算子の使用は、アクセス回数が誤解されやすいためC++20からは非推奨とされました。
今日、Cライブラリの多くの所でこのような操作は利用されており、非推奨化はそれらのライブラリがC++から利用できなくなることを意味します。ベンダ提供のヘッダファイルは多くの場合安定性を優先するために更新されることは稀で、むしろ新しいバージョンのコンパイラを使用しないようにする可能性があります。
また、volatile
値に対する複合代入演算子の仕様を推奨されているように書き直したとき、気づき辛いバグを埋め込むことになる可能性があります
UART1−>UCSR0B |= (1<<UCSZ01 ) ; // 現在使用されているコード UART1−>UCSR0B = UART1−>UCSR0B | (1<<UCSZ01 ) ; // C++20に伴って書き直したコード UART2−>UCSR0B = UART1−>UCSR0B | (1<<UCSZ01 ) ; // それを別のデバイス用にコピペしなおしたコード ^^^^^
このように、別のデバイスの特定のレジスタを読まなければならないのに、コピペミスによって元のデバイスのレジスタを参照し続けてしまいます。このエラーは発見が難しく、volatile
が誤って使用された場合のエラーとコードの冗長化によるこのエラーとを交換しているだけになっています。
これらの理由により、volatile
値に対する複合代入演算子の非推奨化は間違った判断であり、元に戻そうという提案です。
P2337R0 Less constexpr for <cmath>
<cmath>
および<complex>
の数学関数のconstexpr
対応を、ランタイムの実装が正しくなるまで遅らせる提案。
C++における数学関数はIEEE754を参照しているため、そこで定義される正しい丸めによって結果を返す必要がありますが、現在のランタイムの実装は特に32bit浮動小数点数で誤っています。すなわち、ある数学関数に対する同じ入力に対して得られる出力は必ずしも実装間で一致しません。
(色のついた部分が誤った丸めが行われている部分)
これは浮動小数点数計算の移植性を損ねていると同時に、コンパイル時と実行時でその結果が一致しないことが同じ実装においても生じうることを意味しています。
正しいにしても間違っているにしても、ある特定の実装における丸めの結果に依存している様なコードは、コンパイル時計算と実行時計算の間の結果の不一致によって静かなバグを埋め込むことにつながりかねません。
実行時に同様の問題が既に存在していることが分かっているのに、同じ問題を追加するのは避けるべきであり、ランタイムの実装が正しくなるまではconstexpr
対応をしない方がいい、という提案です。
また、C言語に対して進行中のISO/IEC TS 18661-4:2015で提案されている、cr
プリフィックスをもつ関数(正しい丸めによる結果を返すことが保証されている関数)をC++にも追加して、それに対してのみconstexpr
を付加することを代替案として挙げています。
この部分の6割は以下の型のご指摘によって構成されています
P2342R0 For a Few Punctuators More
ドル記号($
)、アットマーク(@
)、バッククオート(`)の3つが将来の機能のためのトークンとして使用可能であることを説明する文書。
AsciiとEBCDICにある記号のうち、$と@と`の3つだけがC++で使用されていません。これらによる構文は将来の提案のために使用できる可能性があります。この文書はその可能性を検討したものです。
バッククオート以外の記号はC++を拡張した言語や外部ツールなどでよく使用されており、そこでの構文とバッティングする可能性が高そうです。どうやら、@<...>, ${...}, $(...)
のような構文は使用可能のようです。
P2345R0 Relaxing Requirements of Moved-From Objects
ムーブ後オブジェクトの要件を緩和する提案。
movable
コンセプトあるいはassignable_from
コンセプトでは、ムーブ後オブジェクトの状態を「有効だが未規定(valid but unspecified)」と定めています。一方で、ライブラリ要件ではムーブ後オブジェクトの状態は単に「未規定(unspecified)」とされています。
ここでの「有効」は定義されていませんが、クラスの不変条件を満たしていて、クラスの全ての操作が規定された振る舞いをしたうえで、未規定の状態、のような意味だと思われます。
しかし実際のところ通常のムーブ操作では「有効」な状態にしておくことは難しく、これを強いることは余計なオーバーヘッドの原因や、ユーザー定義操作の可能性を妨げています。一方で完全に未規定にしてしまうのも望ましくないため、「有効」の意味を詳しく規定することでムーブ後オブジェクトの状態をある程度規定しようとする提案です。
既存の標準ライブラリの実装では、ムーブ後オブジェクトに対して次のような操作だけが必要なようです。
mf.~()
mf = a
mf = move(a)
mf = move(mf)
この提案はムーブ後オブジェクトは少なくともこれらの操作は行えることを要求しようとするものです。
この提案では特に、セルフスワップ(std::swap(a, a)
)をサポートすることに焦点を当てています。セルフスワップはstd::random_shuffe()
などの古い標準ライブラリの実装に現れることがあり、結局はセルフムーブ代入(a = std::move(a)
)操作に帰着します。
ただし、セルフムーブ代入が有効な操作となるのはムーブ後オブジェクトにおいてのみです。a = std::move(b)
の様な代入では、事後条件としてa
はb
の操作前の値を保持し、b
は未規定の値を保持します。しかし、a = std::move(a)
を考慮するとその指定は矛盾しており、それでもその両方の保証が満たされるのはa
が既に未規定の値となっている、すなわちムーブ後オブジェクトである場合のみです。ここに有効性を要求してしまうと、セルフムーブ代入操作は有効な操作とはなりません。
この提案では、ムーブ後オブジェクトに対してのみセルフムーブ代入操作を有効であると規定する案と、より一般のオブジェクトに対してセルフムーブ代入操作を有効であると規定する案の二つのどちらかを選択することを提案しています。
P2348R0 Whitespaces Wording Revamp
ユニコードの使用に従う形で、改行と空白を明確に定義する提案。
この提案では以下の点が変更されます。
- whitespaceを文法用語として導入する
- whitespaceという言葉は別に定義するwhitespaceの集合を参照する
- 垂直タブは
//
コメントを終了することを規定 - new-lineをline-breakで置き換える
- new-lineはLFによる改行文字を示すようになる
- 翻訳フェーズ1-7まで、コメントを含めたすべてのホワイトスペースが保存されるようになる
- ただし、そのことは観測可能ではない
- 文字の集合は、現在のものから拡張しない
- ホワイトスペースとみなされるものを追加しない
- ただし、CRLFが一つの改行(line-break)とみなされるようになる
- line-breakは生文字列リテラル中ではLF(new-line)にマップされる
変更は多岐にわたりますが、破壊的な変更は意図されていません。
P2350R0 constexpr class
constexpr
対応クラスの簡易構文の提案。
C++20にてstd::vector/std::string
の全てのメンバがconstexpr
対応され、そのほかのコンテナやスマートポインタも対応させる提案が提出されています。
クラスメンバ関数をconstexpr
対応させるにはすべてのメンバ関数にconstexpr
を付けて回らねばならず、冗長な記述となっています。
この提案では、クラス宣言にconstexpr
を付加することでクラスのメンバがすべて暗黙constexpr
関数となるようにする構文を提案しています。
C++20 | この提案 |
---|---|
class SomeType { public: constexpr bool empty() const { /* */ } constexpr auto size() const { /* */ } constexpr void clear() { /* */ } }; |
class SomeType constexpr { public: bool empty() const { /* */ } auto size() const { /* */ } void clear() { /* */ } }; |
これは丁度final
を指定できるところにおけるようにするものです。
また、これはちょうどそのクラスのみに作用するように提案されています。つまり、派生先や基底クラスには影響を与えません。
他にもconst
やconsteval
も考えられますが、この提案ではconstexpr
に絞られています。
P2352R0 SG16: Unicode meeting summaries 2020-12-09 through 2021-03-24
SG16(Unicode study group)の2020/12/09-2021/03/24までの間のミーティングにおける議事録。
P2353R0 Metaprograms and fragments are needed in comma-separated contexts
P2237で提案されたmetaprogramとinjectionをテンプレートパラメータなど、カンマ区切りリストの文脈でも使用可能に拡張する提案。
P2237およびP0712では、consteval
を用いたシンプルなコードジェネレーション機能であるmetaprogramを導入しました。
consteval { for (int i = 0; i < 10; ++i) { generate_some_code(i); } }
generate_some_code()
はここにソースコードを注入するconsteval
関数です。metaprogramは無名変数の初期化子として呼び出される無名のconsteval
関数と捉えることができます。
P2237ではmetaprogramを発展させたコードfragmentのinjectionを提案しています。
template<struct T> class Interface { public: consteval { // リフレクションによってTのメンバ変数をイテレートする template for (auto data : meta::data_members_of(^T)) // "class fragment"のinjection // Tの各メンバに対するgetterの純粋仮想関数宣言をinjectionする << <class { virtual typename [:meta::type_of(data):] |#"get_" + meta::name_of(data)#|() const = 0; }>; } }; consteval void inject_field() { // class fragment injection << <class { int i = 4; }>; } consteval void inject_vardecl() { // statement injection << <{ int i = 4; }>; } class A { // int i = 4; というメンバ変数宣言をinjection consteval { //inject_vardecl(); //ERROR inject_field(); } }; void f() { // int i = 4; という変数宣言をinjection consteval { //inject_field(); //ERROR inject_vardecl(); } }
これらの構文は名前空間スコープやクラススコープ、ブロックスコープ内でのみ使用可能なものとして提案されており、それ以外の所、とくにテンプレートパラメータリストをはじめとするカンマ区切りリスト内では使用できませんでした。
この提案は、consteval{}
を書くことのできるコンテキストを拡張したうえで、新しいいくつかのfragmentを追加することを提案しています。そして、fragmentの種類を増やしたことで、P2237の構文の拡張が困難になったことから、^<K>{}
の形式の新しいfragmentの構文も提案しています。
// ステートメントと式のfragment ^<frag::stmt>{ [:m:] = 42; } // was <{ [:m:] = 42; }> ^<frag::expr>{ 3, [:m:] + 4 } // was <( 3, [:m:] + 4 )> // この提案で追加される新しいfragment ^<frag::parm>{ int i, int j } ^<frag::tparm>{ typename T, int N } ^<fram::targ>{ [:Trefl:], [:Nrefl:] } ^<frag::init>{ 3, .[:m:] = 4 } ^<frag::cinit>{ [:m:](3), [:n:](4) } ^<frag::base>{ public Foo, private virtual [:Barrefl:] }
P2355R0 Postfix fold expressions
可変長テンプレートの畳み込み式において、() []
の2つの演算子を使用可能にする提案。
[]
は添え字演算子に複数の引数を渡せるようにする提案(P2128R3)の検討で発案され、言語サポートによってそれを達成する代わりに使用できる可能性があります。
// P2128R3による複数引数をとる[]演算子 decltype(auto) index(auto &arr, auto ...args) { return arr[args...]; // この様に展開 return arr[arg1, arg2, ..., argN]; } // この提案によるプロクシベース[]演算子 decltype(auto) index(auto &arr ,auto ...args) { return (arr[...][args]); // この様に展開 return arr[arg1][arg2]...[argN]; }
この[]
による構文と展開を自然に()
に拡張することができます。それによって、畳み込み式で利用するためだけに演算子オーバーロードを使用するようなワークアラウンドをいくらか簡単にすることができます。
namespace detail { template<class F> struct call { F &&f; template<class T> decltype(auto) operator|(T &&t) const { return std::forward<F>(f)(std::forward<T>(t)); } }; } template<class T, class X> decltype(auto) nest_tuple(T &&t,X &&x) { return std::apply( [&x]<class ...TT>(TT &&...tt) -> decltype(auto) { return (detail::call<TT>{std::forward<TT>(tt)} | ... | std::forward<X>(x)); // forwrdとかは無視して、展開は次のようになる return tt1 | (tt2 | (... | (ttN | x))); }, std::forward<T>(t)); } // ↑を↓こう書けるようになる template<class T,class X> decltype(auto) nest_tuple(T &&t,X &&x) { return std::apply( [&x]<class ...TT>(TT &&...tt) -> decltype(auto) { return (std::forward<TT>(tt)(...(std::forward<X>(x))); // forwrdとかは無視して、展開は次のようになる return tt1(tt2(...(ttN(x)))); }, std::forward<T>(t)); }
これらの利点から、畳み込み式で[] ()
を使えるようにしようとする提案です。
パラメータパックをpack
、[] or ()
による呼び出しが可能な型のオブジェクトをc
、任意の初期項をa
とすると、この提案による拡張は次の様な構文になります
(pack[...]); // 単項右畳み込み (...[pack]); // 単項左畳み込み (pack[...[a]]); // 二項右畳み込み (c[...][pack]); // 二項左畳み込み (pack(...)); // 単項右畳み込み (...(pack)); // 単項左畳み込み (pack(...(a))); // 二項右畳み込み (c(...)(pack)); // 二項左畳み込み
pack
の中身をarg1, arg2, ..., argN
として、それぞれ次のように展開されます
arg1[arg2[arg3[...[argN]]]]; // 単項右畳み込み arg1[arg2][arg3]...[argN]; // 単項左畳み込み arg1[arg2[...[argN[a]]]]; // 二項右畳み込み c[arg1][arg2]...[argN]; // 二項左畳み込み arg1(arg2(arg3(...(argN)))); // 単項右畳み込み arg1(arg2)(arg3)...(argN); // 単項左畳み込み arg1(arg2(...(argN(a)))); // 二項右畳み込み c(arg1)(arg2)...(argN); // 二項左畳み込み
複雑ではありますが、()
と[]
は対応する構文によって同じ記述が可能で、その構文によって従来の4つの畳み込みのいずれかに帰着され、適用される演算子op
を[] ()
に変更しパックの要素を包み込むように展開されます。
さらに、これらの構文による二項畳み込みの...
の後の()
内には、pack, lists...
の形のリストを書くことができます。
(pack(...(a, b, c))); // 二項右畳み込み (c(...)(pack, a, b, c)); // 二項左畳み込み
これは次のように展開されます
arg1(arg2(...(argN(a, b, c)))); // 二項右畳み込み c(arg1, a, b, c)(arg2, a, b, c)...(argN, a, b, c); // 二項左畳み込み
[]
の時はこのリストを{}
で包む必要がある以外は、()
と同様になります。
(pack[...[{a, b, c}]]); // 二項右畳み込み (c[...][{pack, a, b, c}]); // 二項左畳み込み
P2356R0 Implementing Factory builder on top of P2320
Factoryパターンを自動的に実装する方法についてのプレゼンテーション資料。
これはSG7のメンバに向けて、現在提案中のリフレクションを用いて、外部のメタデータから任意のクラスのオブジェクトを構築するコードを生成するメタプログラミング手法について解説されています。
P2358R0 Defining Contracts
SG21で議論されているContracts関連の用語を定義する文書。
これはContractsについて何か設計を提案するものではなく、SG21の議論で頻出する概念について、対応する言葉と意味を定義しておくものです。
主に想定されるバグの種類、契約違反を検出したときの振る舞いの各候補についてなどに名前を当て意味を説明しています。
P2360R0 Extend init-statement to allow alias-declaration
if for
などのinit-statementに、using
宣言を書けるようにする提案。
C++17でif switch
、C++20で範囲for
の構文が拡張され、init-statementという初期化領域を置けるようになりました。
そこには通常の変数の初期化宣言の他にtypedef
宣言も書くことができますが、なぜかusing
は書けません。
この提案はusing/typedef
の一貫性を向上させるために、init-statement内でusing
によるエイリアス宣言を書けるようにする提案です。
// C++20 for (typedef int T; T e : v) { ... } // この提案 for (using T = int; T e : v) { ... }
モダンC++ではtypedef
よりもusing
の使用が推奨されていますが、init-statementはそれができない唯一の場所のようです。
また、対応としては逆にinit-statementにおけるtypedef
を禁止するという方法もありますが、typedef
のスコープを制限するために利用されているらしく、using
でも同じことができるようにすることを提案しています。
P2361R0 Unevaluated string literals
コンパイル時にのみ使用され、実行時まで残らない文字列リテラルについての扱いを明確化する提案。
_Pragma, asm, extern, static_assert, [[deprecated]], [[nodiscard]]
など、文字列リテラルはコンパイル時にのみ使用される文脈に現れることができます。このような文字列はナローエンコーディングやエンコーディングプリフィックスで指定されたエンコーディングに変換されるべきではありません。
そのため、これらの文字列にはユニコード文字を含めることができる点を除いて、エンコーディングに関して制約されるべきではありません。
しかし、現在これらの文字列は区別されることなく実行時にも使用される文字列と同じ扱いを受けており、エンコーディングの制約も同様です。
この提案は、コンパイル時メッセージなどでユニコードを活用できるようにするために、コンパイル時にのみ使用される文字列について特別扱いするようにする提案です。
この提案は、コンパイル時にのみ使用される文字列について次のように扱われるようにします
- プリフィックスを許可しない
- 文字列は実行時エンコーディングに変換されない
- ユニバーサル文字名と単純なエスケープシーケンス(
\0
を除いて)は対応するユニコード文字に置換され、その他のエスケープシーケンスは許可されない
この変更は破壊的なものですが、オープンソースのコードベース調査ではほとんど影響がないようです。
P2362R0 Make obfuscating wide character literals ill-formed
エンコード可能ではない、あるいは複数文字を含むワイド文字リテラルを禁止する提案。
文字リテラルには複数の文字を指定することができ、それはワイド文字リテラルにおいても同様です。ワイド文字リテラルではそれに加えて、1文字が1つのコード単位に収まらない文字リテラルを書くことができます。
wchar_t a = L'🤦<200d>♀️'; // \U0001f926 wchar_t b = L'ab'; // multi character literal wchar_t c = L'é́'; // \u0065\u0301
上記のa
はwchar_t
のサイズが4バイトである環境(Linuxなど)では書いたままになりますが、2バイトの環境(Windowsなど)だと表現しきれないためUTF-16エンコーディングで読み取られた後に、上位か下位の2バイトが最終的な値として取得されます(Windowsは上位2バイトが残る)。
b
はマルチキャラクタリテラルと呼ばれるもので、どの文字が残るか、あるいはどういう値になるかは実装定義とされます。MSVCでは最初の文字が、GCC/Clangでは最後の文字が残るようです。
c
は2つのユニコード文字から構成されており、これもマルチキャラクタリテラルの一種です。これは1文字で同じ表現ができる文字がユニコードに存在していますが(\u00e9
)、e
と́́
の2文字を組み合わせて表現することもでき、後者の場合は表示上は1文字ですが、1コード単位ではなく2コード単位の文字列となります。
このように、これらの文字列の扱いは実装間で一貫しておらず移植性もなく、視認しづらいことからバグの原因となりえるため、禁止しようという提案です。
ただし、wchar_t
のサイズが4バイトである環境の上記a
のケースは適正であるため、引き続き使用可能とされます。
これは破壊的変更となりますが、コンパイラのテストケースを除いて、オープンソースのコードベースでは使用されているコードは発見できなかったようです。
P2363R0 Extending associative containers with the remaining heterogeneous overloads
連想コンテナの透過的操作を、さらに広げる提案。
C++20では、非順序連想コンテナに対して透過的な検索を行うことのできるオーバーロードが追加されました。「透過的」というのは連想コンテナのキーの型と直接比較可能な型については、一時オブジェクトを作成することなくキーの比較を行う事が出来ることを指します。これによって、全ての連想コンテナで透過的な検索がサポートされました。
現在、C++23に向けて削除の操作に関して同様にしようとする提案(P2077R2)がLWGにおいて議論中です。
この提案は、その対象さらに広げて、以下の操作を透過的にするものです。
std::set/std::unorderd_set
のinsert
std::map/std::unorderd_map
のinsert_or_assign/try_emplace/operator[]/at
- 非順序連想コンテナの
bucket
この提案の後では、これらの操作の際にKey
と異なる型の値について一時オブジェクトの作成が回避されるようになり、パフォーマンス向上が期待できます。