文書の一覧
全部で39本あります。
- N4894 Business Plan and Convener's Report
- P0288R8 move_only_function (was any_invocable)
- P0847R7 Deducing this
- P1206R4 Conversions from ranges to containers
- P1726R5 Pointer lifetime-end zap (informational/historical)
- P2036R2 Changing scope for lambda trailing-return-type
- P2066R8 Suggested draft TS for C++ Extensions for Minimal Transactional Memory
- P2093R7 Formatted output
- P2167R1 Improved Proposed Wording for LWG 2114 (contextually convertible to bool)
- P2198R2 Freestanding Feature-Test Macros and Implementation-Defined Extensions
- P2242R3 Non-literal variables (and labels and gotos) in constexpr functions
- P2249R1 Mixed comparisons for smart pointers
- P2273R2 Making std::unique_ptr constexpr
- P2290R2 Delimited escape sequences
- P2295R5 Support for UTF-8 as a portable source file encoding
- P2300R1 std::execution
- P2316R1 Consistent character literal encoding
- P2338R1 Freestanding Library: Character primitives and the C library
- P2347R1 Argument type deduction for non-trailing parameter packs
- P2350R1 constexpr class
- P2362R1 Remove non-encodable wide character literals and multicharacter wide character literals
- P2392R1 Pattern matching using "is" and "as"
- P2401R0 Add a conditional noexcept specification to std::exchange
- P2402R0 A free function linear algebra interface based on the BLAS (slides)
- P2403R0 Presentation on P2300 - std::execution
- P2404R0 Relaxing equality_comparable_with's and three_way_comparable_with's common reference requirements to
- P2405R0 nullopt_t and nullptr_t should both have operator and operator==
- P2406R0 Fix counted_iterator interaction with input iterators
- P2407R0 Freestanding Library: Partial Classes
- P2408R0 Ranges views as inputs to non-Ranges algorithms
- P2409R0 Requirements for Usage of C++ Modules at Bloomberg
- P2410R0 Type-and-resource safety in modern C++
- P2411R0 Thoughts on pattern matching
- P2412R0 Minimal module support for the standard library
- P2413R0 Remove unsafe conversions of unique_ptr
- P2414R0 Pointer lifetime-end zap proposed solutions
- P2415R0 What is a view?
- P2416R0 Presentation of requirements in the standard library
- P2417R0 A more constexpr bitset
- おわり
N4894 Business Plan and Convener's Report
ビジネスユーザ向けのC++およびWG21の現状報告書。
P0288R8 move_only_function
(was any_invocable)
ムーブのみが可能で、関数呼び出しのconst
性やnoexcept
性を指定可能なstd::function
であるstd::any_invocable
の提案。
以前の記事を参照
このリビジョンでは、P2265R1を受けて、名前をany_invocable
からmove_only_function
へ変更した事と配置するヘッダを独自ヘッダから<functional>
へ変更した事などです。
この提案はこのリビジョンを持ってLWGに転送され、C++23入りを目指してLWGでレビューされます。
P0847R7 Deducing this
クラスのメンバ関数の暗黙のthis
引数を明示的に書けるようにする提案。
以前の記事を参照
- P0847R5 Deducing this - [C++]WG21月次提案文書を眺める(2020年10月)
- P0847R6 Deducing this - [C++]WG21月次提案文書を眺める(2021年01月)
このリビジョンでの変更は、CWGのレビューを受けて提案する文言を変更した事です。
この提案はCWGでのレビューを終え、次の全体会議で投票にかけられることが決まっています。何事もなければそこでC++23に入ります。
P1206R4 Conversions from ranges to containers
任意のrangeをコンテナへ変換/実体化させるためのstd::ranges::to
の提案。
- P1206R2 ranges::to: A function to convert any range to a container - [C++]WG21月次提案文書を眺める(2020年10月)
- P1206R3 ranges::to: A function to convert any range to a container - [C++]WG21月次提案文書を眺める(2020年11月)
このリビジョンでの変更は、
- コンテナ型を
range
から構築する為のタグ型std::from_range_t
とそれを受け取るコンストラクタを標準の多くのコンテナへの追加 ranges::to
の提案する文言の改善ranges::to
による構築時、可能なら構築対象コンテナの.reserve()
を呼び出すようにした- 動機の項目を書き直した
ことなどです。
P1726R5 Pointer lifetime-end zap (informational/historical)
Pointer lifetime-end zapと呼ばれる問題の周知とそれについてのフィードバックを得るための報告書。
以前の記事を参照
このリビジョンでの変更は、zapという言葉を、provenanceとlifetime-end pointer zapの適用可能な側面の和集合として定義しなおし、それに従って文書を書き直したことなど、ポインタのprovenanceの概念に関連する説明を追加した事や、解決策に関する事を追記したことなど、多岐に渡ります。
この文書は提案ではありませんが、この問題の解決策がP2414R0で提案されています。
P2036R2 Changing scope for lambda trailing-return-type
ラムダ式の後置戻り値型指定において、初期化キャプチャした変数を参照できるようにする提案。
以前の記事を参照
このリビジョンでの変更は、機能テストマクロ(追加しない)と実装経験(ない)についての説明のセクションを追記した事です。
P2066R8 Suggested draft TS for C++ Extensions for Minimal Transactional Memory
現在のトランザクショナルメモリTS仕様の一部だけを、軽量トランザクショナルメモリとしてC++へ導入する提案。
以前の記事を参照
- P2066R2 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年05月)
- P2066R3 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年09月)
- P2066R4 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年10月)
- P2066R5 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年02月)
- P2066R6 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年03月)
- P2066R7 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年05月)
このリビジョンの変更点は、LEWGでのレビューのために「notable design decisions」を追加した事です。
P2093R7 Formatted output
std::format
によるフォーマットを使用しながら出力できる新I/Oライブラリstd::print
の提案。
以前の記事を参照
- P2093R0 Formatted output - [C++]WG21月次提案文書を眺める(2020年6月)
- P2093R1 Formatted output - [C++]WG21月次提案文書を眺める(2020年7月)
- P2093R2 Formatted output - [C++]WG21月次提案文書を眺める(2020年10月)
- P2093R3 Formatted output - [C++]WG21月次提案文書を眺める(2021年1月)
- P2093R4 Formatted output - [C++]WG21月次提案文書を眺める(2021年2月)
- P2093R5 Formatted output - [C++]WG21月次提案文書を眺める(2021年3月)
- P2093R6 Formatted output - [C++]WG21月次提案文書を眺める(2021年4月)
このリビジョンでの変更は
- SG16での投票結果の追記
- 最新のWDをベースとするように変更
- これによって、フォーマットのコンパイル時チェックが導入された
- 無効なコードポイントの置換について、ユニコード規格を参照するようにした
- 「The Unicode® Standard Version 13.0 – Core Specification」についての参照を提案する文言に追加
- 文字コードが混在しているときの動作を明確にした
ことなどです。
P2167R1 Improved Proposed Wording for LWG 2114 (contextually convertible to bool)
contextually convertible to boolと言う規格上の言葉を、C++20で定義されたboolean-testable
コンセプトを使用して置き換える提案。
必要なかったため、std::valarray
の比較演算子に対する変更を削除した事、タイトルを改善したことなどです。
P2198R2 Freestanding Feature-Test Macros and Implementation-Defined Extensions
フリースタンディング処理系でも使用可能なライブラリ機能について、機能テストマクロを追加する提案。
以前の記事を参照
- P2198R0 Freestanding Feature-Test Macros and Implementation-Defined Extensions - [C++]WG21月次提案文書を眺める(2020年07月)
- P2198R1 Freestanding Feature-Test Macros and Implementation-Defined Extensions - [C++]WG21月次提案文書を眺める(2020年10月)
このリビジョンでの変更は、提案するポリシーの推奨事項の変更や、提案する文言の変更などです。
P2242R3 Non-literal variables (and labels and gotos) in constexpr functions
constexpr
関数において、コンパイル時に評価されなければgoto
やラベル、非リテラル型の変数宣言を許可する提案。
以前の記事を参照
- P2242R0 Non-literal variables (and labels and gotos) in constexpr functions - [C++]WG21月次提案文書を眺める(2020年10月)
- P2242R1 Non-literal variables (and labels and gotos) in constexpr functions - [C++]WG21月次提案文書を眺める(2021年02月)
- P2242R2 Non-literal variables (and labels and gotos) in constexpr functions - [C++]WG21月次提案文書を眺める(2021年03月)
このリビジョンでの変更は、提案する文言の修正とサンプルコードを変更した事です。
この提案は既にCWGのレビューを終えており、次の全体会議で投票にかけられることが決まっています。
P2249R1 Mixed comparisons for smart pointers
スマートポインターの比較演算子に生ポインタとの直接比較を追加する提案。
以前の記事を参照
このリビジョンでの変更は、LEWGでのレビューを受けて設計の根拠を明確にしたこと、提案する演算子オーバーロードをHidden Friendsにしたことなどです。
P2273R2 Making std::unique_ptr constexpr
std::unique_ptr
を全面的にconstexpr
対応する提案。
以前の記事を参照
- P2273R0 Making std::unique_ptr constexpr - WG21月次提案文書を眺める(2020年12月)
- P2273R1 Making std::unique_ptr constexpr - WG21月次提案文書を眺める(2021年04月)
このリビジョンでの変更は、std::make_unique_for_overwriteのconstexpr
実装可能性について説明を追記した事、LEWGでの投票の結果を記載した事などです。
どうやら、2つのunique_ptr
の順序付け比較演算子は除外されるようです(nullptr
との比較は提案されている)。
P2290R2 Delimited escape sequences
文字・文字列定数中の8進・16進エスケープシーケンスおよびユニバーサル文字名について、その区切りが明確になるような形式を追加する提案。
前回の記事を参照
- P2290R0 Delimited escape sequences - [C++]WG21月次提案文書を眺める(2021年02月)
- P2290R1 Delimited escape sequences - [C++]WG21月次提案文書を眺める(2021年06月)
このリビジョンでの変更は、提案する文言の改善と機能テストマクロが不要であることの説明の追記、この提案がWG14へも提出されたことについて記載されたことなどです。
SG22でのミーティングでは、そこにいるWG14メンバはこの提案をCで採用することに前向きなようです。
P2295R5 Support for UTF-8 as a portable source file encoding
C++コンパイラが少なくともUTF-8をサポートするようにする提案。
以前の記事を参照
- P2295R0 Correct UTF-8 handling during phase 1 of translation - WG21月次提案文書を眺める(2021年02月)
- P2295R3 Support for UTF-8 as a portable source file encoding - WG21月次提案文書を眺める(2021年04月)
- P2295R4 Support for UTF-8 as a portable source file encoding - WG21月次提案文書を眺める(2021年06月)
このリビジョンでの変更は、SG16のガイダンスに従って提案する文言を改善した事です。
この提案はSG16からEWGへ、C++23に導入することを目指して転送されました。
また、SG22のミーティングでもこの提案が紹介され、そこにいたメンバーはCとC++がこの提案について一貫したことを採用する点で合意が形成されているようです。
P2300R1 std::execution
P0443R14のExecutor提案をベースにした、任意の実行コンテキストで任意の非同期処理を実行するためのフレームワークの提案。
以前の記事を参照
このリビジョンでの変更は
sender_of
コンセプトの追加scheduler
の特性を取得するCPO、std::this_thread::execute_may_block_caller
の追加- その
scheduler
が現在のスレッドをブロックするかをbool
で取得する
- その
scheduler
の特性を取得するCPO、get_forward_progress_guarantee
の追加- その
scheduler
によって作成された実行エージェントが、forward progress guaranteeを満たすかを列挙値で取得する
- その
unschedule
アダプタの削除- typoやバグの修正
などです。
P2316R1 Consistent character literal encoding
#if
プリプロセッシングディレクティブの条件式において、文字リテラルをC++の式の意味論と同等に扱えるようにする提案。
以前の記事を参照
このリビジョンでの変更は、EWGの要請によりコンパイラ実装者に連絡し実装に問題が無い事を確認したことを追記した事と、機能テストマクロが必要ない理由を記載したことなどです。
SG22のWG14メンバは、この提案をWG14でも採用することに前向きなようです。
P2338R1 Freestanding Library: Character primitives and the C library
<charconv>
とstd::char_traits
をはじめとするいくつかのヘッダをフリースタンディングライブラリ指定する提案。
以前の記事を参照
このリビジョンでの変更は
- POSIXエラーのハンドリングについて追記
- WG14が
intmax_t
を非推奨としているため、<cinttypes>
を除外 - SDCCでの実装状況の確認に関する追記
- 一部の機能をオプションとする代替案についての説明の追記
wchar_t
関連関数の説明の追記
などです。
P2347R1 Argument type deduction for non-trailing parameter packs
関数テンプレートの実引数型推論時に、引数リスト末尾にないパラメータパックの型を推論できるようにする提案。
以前の記事を参照
このリビジョンでの変更は提案する文言やサンプルコードの修正です。
P2350R1 constexpr class
constexpr
対応クラスの簡易構文の提案。
以前の記事を参照
このリビジョンでの変更は指定子の現れる順番についての説明の追記と提案する文言の修正です。
この提案では、クラス名の後に続くときは必ず: final constexpr
となるように、final
とconstexpr
の指定に順序を設けています。これによって実装が簡単になり教えやすくなるため、順序を自由にする必要はないとの主張です。
P2362R1 Remove non-encodable wide character literals and multicharacter wide character literals
エンコード可能ではない、あるいは複数文字を含むワイド文字リテラルを禁止する提案。
以前の記事を参照
このリビジョンでの変更は、タイトルの変更、提案する文言の改善、機能テストマクロについての追記などです。
この提案はSG16からEWGへ、C++23に向けて導入ることを目指して転送されました。
P2392R1 Pattern matching using "is" and "as"
現在のパターンマッチングをクリーンアップし、使用可能な所を広げる提案。
以前の記事を参照
このリビジョンでの変更は
などです。
EWGでの投票では、この提案に作業時間をかける事(おそらくC++23に向けて)について合意がとれています。
P2401R0 Add a conditional noexcept
specification to std::exchange
std::exchange
にnoexcept
指定を追加する提案。
std::exchange
はその動作のほぼ全てが指定されていますが、noexcept
は指定されていません。
template<class T, class U = T> constexpr T exchange(T& obj, U&& new_val) { T old_val = std::move(obj); obj = std::forward<U>(new_val); return old_val; }
このため、std::exchange
を使用してムーブコンストラクタを実装した時など、自然にnoexcept
になるべきところでならなくなってしまっています。また、noexcept(std:exchange(...))
はfalse
となり、そのようにnoexcept
指定をする場合もnoexcept
になりません。
指定されている実装を見れば、例外を投げ売るのは1行目のT
のムーブ構築と、2行目のU -> T
のムーブ代入です。そのため、std::exchange
が例外を投げるかどうかはstd::is_nothrow_move_constructible<T>
とstd::is_nothrow_assignable<T&, U>
によって簡単に求めることができるため、標準にもそう指定することを提案しています。
これらのことはすでにMSVC STLでは実装されています。
P2402R0 A free function linear algebra interface based on the BLAS (slides)
P1673R3 A free function linear algebra interface based on the BLASの解説スライド。
どういう動機からBLASベースのAPIを追加し、どのような設計になって、どう変化してきたのか、等を解説しています。
P2403R0 Presentation on P2300 - std::execution
P2300R1 std::execution
の機能紹介スライド。
P2300によって導入される非同期処理を構成するための各種アルゴリズムの解説が行われています。
P2404R0 Relaxing equality_comparable_with's and three_way_comparable_with's common reference requirements to
各種異種比較を定義するコンセプトのcommon_reference
要件を緩和する提案。
異種比較系コンセプトとは、std::equality_comparable_with
、std::three_way_comparable_with
、std::totally_ordered_with
の3つです。これらのコンセプトは2つの型の間にそれぞれが表現する二項関係が成り立っている事を表すコンセプトです。
しかし、これらのコンセプトは現在のところ、ムーブオンリーな型について正しく機能していません。
// Tはなんか型とする static_assert(std::equality_comparable_with<std::unique_ptr<T>, std::nullptr_t>); // コンパイルエラーとなる int main() { std::unique_ptr<int> p; p == nullptr; // OK }
これらのコンセプトはその定義中にstd::common_reference_with
コンセプトによる制約を含んでおり、そのcommon_reference_with
コンセプトが型に対して実質的にcopyable
である事を要求しているために起こります。
3つのコンセプト定義にはどれも、std::common_reference_with<const std::remove_reference_t<T>&, const rstd::emove_reference_t<U>&>
のような制約式があります。remove_reference_t
の部分を単にT, U
に置き換えるとstd::common_reference_with<const T&, const U&>
となります。std::common_reference_with<T, U>
はT, U
がどちらもそれらのcommon_reference
であるCR
に変換可能(std::convertible_to<CR>
)である事を指定しています。
std::common_reference_t<const std::unique_ptr<T>&, const std::nullptr_t&>
はstd::unique_ptr<T>
になるので、std::unique_ptr<T>
とstd::nullptr_t
に対してはstd::convertible_to<const std::unique_ptr<T>&, std::unique_ptr<T>>
(const std::unique_ptr<T>&
-> std::unique_ptr<T>
への変換)が要求されることになりますが、これはコピーコンストラクタを呼び出し、std::unique_ptr
はムーブオンリーなので変換可能ではないためcommon_reference
要件を満たすことができず、異種比較系コンセプトはfalse
となります。
一般化するとこのことは、common_reference_t<const T&, const U&>
がT
となり、T(const U&)
のコンストラクタは利用可能でないがT(U&&)
は利用可能であるようなT, U
について同じことになります。これはT, U
を逆にしても同じ事です。
この3種類のコンセプトは<ranges>
を始め色々な所で使われており、また基礎的なコンセプトであるため色々な所で使われていくでしょう。すると、意図せずこの問題に遭遇する確率は上がっていく事でしょう。
このcommon_reference
要件は同値関係についての数学的な考察から来ているようです。ある集合AとBの間に同値関係を定義することは、代わりにその和集合A ∪ Bの上に同値関係を定義する場合にのみ意味を持ちます。そのことに基づいてC++では、型T, U
の間の同値関係はT
とU
に共通する何らかのsupertypeの上で動作している、ととらえます。このsupertypeを導出するのがcommon_reference
であり、common_reference
要件はsupertype上で同値関係が定義されている事を要求しています。そのため、実行時にcommon_reference
への変換が発生する、もしくは必要となるわけではありません。
common_reference
要件の問題点は、このsupertypeの要求を2つの型の間のcommon_reference
として表現してしまっていることにあります。CV修飾や形成可能な参照型によらずにsupertype要件を表現できれば、この問題を解決する事ができ、コンセプトはより洗練されます。
この問題は次の2つの問題に分けて考える事ができます(C = std::common_reference_t<const T&, const U&>
とします)。
T
がムーブオンリーでC
とT
が同じ型となるC
はT
ではなく、T
の右辺値からのみ構築できる
これらの問題の両方で、T(U)
はC
に変換可能である必要がありますが、それは数学的な要件であって実行時に実際に変換されません。そのため、変換関係を表現するためにおかしな事をする必要はありません。
1つ目のケースは、C
とT
はCV参照修飾を除いて同じ型であることがわかるため、convertible_to<const T&, C>
の要件を緩和して、const T&
とC
をremove_cvref
した後で同じ型になる場合を受け入れるようにすることで解決できます。これは、const T&
がconst C&
と同じ型であるときはconst T& -> const C& -> C
のような変換(const T&
をC
の一時オブジェクトにバインドすることでC
を構築)が可能であるためです。これは実際にやったら危険なことですが、実際にはこの変換は行われません。
2つ目のケースは、convertible_to<const T&, C>
を緩和してconvertible_to<const T&, C> || convertible_to<T&&, C>
のように、T
のコピーを必要としない有効な変換を探すようにすることで解決できます。実際こんな変換を勝手にやられたら困りますが、ここでもやはり実行時にこのような変換は行われません。
これらの解決は、T
をU
に置き換えて同じ事が言えます。
この提案は、これらの事を考慮したsupertype要件を表現するcommon-comparison-supertype-with<T, U>
という説明専用のコンセプトによって現在の異種比較系コンセプトのcommon_reference
要件を置き換えることでこのような問題の解決を図るものです。その際、構文的要件だけでなく意味論要件の変更も必要となります。
この変更は破壊的なものですが、影響を受けるのは極端なコードだけであり、ムーブオンリータイプで異種比較系コンセプトが正しく動作するようになる利点が上回ると筆者の方は主張しています。実際、libc++とMSVC STLの内部テストを用いてこの提案による変更の実装をテストしたところ、この提案の変更によって失敗するテストはなかったようです。
P2405R0 nullopt_t
and nullptr_t
should both have operator and operator==
nullopt_t
とstd::optional<T>
、nullptr_t
とstd::unique_ptr<T>, std::shared_ptr<T>
の間で各種異種比較を定義するコンセプトが動作するようにする提案。
nullopt_t
とstd::optional<T>
は<=>/==
によって比較することができます。しかし、異種比較系のコンセプト(std::equality_comparable_with, std::three_way_comparable_with, std::totally_ordered_with
)はそれらの型についてfalse
となります。
// Tはなんか型とする static_assert(std::three_way_comparable_with<std::optional<T>, std::nullopt_t>); // NG static_assert(std::equality_comparable_with<std::optional<T>, std::nullopt_t>); // NG int main() { std::optional<int> opt; // ともにOK auto cmp = opt <=> std::nullopt; auto eq = opt == std::nullopt; }
先ほどP2404R0と似た問題に見えますが、これはnullopt_t
自身に何ら比較演算子が定義されていないことによリます。異種比較系のコンセプトは型T, U
の間の比較についてT
およびU
自身がまず同等の比較演算が可能である事を求めます。nullopt_t
はそうではないため、実際の比較可能性とは異なる結果を生成してしまっています。
前述のように、これらのコンセプトは<ranges>
を始め色々な所で使われているためこれらのコンセプトがきちんと機能していないと、optional
とnullopt
について比較が必要となるところでコンパイルエラーを起こしてしまいます。
これらの事は、nullptr_t
とスマートポインタの間にも同じことが言えます。
この提案は、nullopt_t
とnullptr_t
に<=>/==
による比較を定義することでこれらの問題の解決を図るものです。どちらの比較もstd::strong_oredering::equal
を返すdefault
の<=>
を定義した形の比較となり、自身との同値性について(== <= >=
)だけtrue
を返すものです。
なお、nullptr_t
とスマートポインタの間でこれらコンセプトをきちんと動作させるには、先ほどのP2404の解決が同時に必要となります。
P2406R0 Fix counted_iterator
interaction with input iterators
std::counted_iterator
を安全に使用可能にする提案。
次のプログラムは実行完了するのに非常に時間がかかります。
#include <ranges> #include <iostream> int main() { for (auto i : std::views::iota(0) | std::views::filter([](auto i) { return i < 10; }) | std::views::take(10)) { std::cout << i << '\n'; } }
やっていることは0~9の整数値を出力しているだけです(色々突っ込みどころはありますが)。
このviews::take
は前段のrange
(イテレータ)がrandom_access
でない場合にstd::counted_iterator
を使用しています。問題があるのは2段目のviews::filter
の条件です。
非常にわかりづらいですが、これは範囲for文によってイテレータ操作に展開されており、for文によるループは終了する時に最後の要素の次までイテレータが進行します。ループが9
を出力した後、次のようなことが起こることが期待されます。
実際にはviews::filter
の条件がi < 10
であることによって、上記手順の1-1、views::filter
のインクリメントは9を超え10未満の要素(整数)を探索し続けます。従って、この探索は終わることが無く(最大値に到達すると終わるのかもしれません・・・)、views::take
のイテレータのインクリメントは終了しません。
この例は作為的ではありますが、同じことはviews::filter
の返す要素の数が不透明である場合に起こり得ます。すなわち、views::filter
によってフィルタされて残る要素の数がtake
する数よりも小さい場合、同じことが起こり終了しない可能性があります。
このことが実際に問題となるのはむしろ、basic_istream_view
のようなinput_iterator
に対して使用したときです。
#include <ranges> #include <iostream> #include <sstream> int main() { auto iss = std::istringstream("0"); // 0を読んだ後、takeイテレータが進行するが、istream_viewが次のストリーム入力を待機するために終了しない for (auto i : std::ranges::istream_view<int>(iss) | std::views::take(1)) { std::cout << i << '\n'; } }
input_iterator
が終端に達した後でインクリメントされると何が起こるのかはイテレータによるため、これはもっとわかりづらい形で顕在化するかもしれません。
この提案は、内部カウンタが0付近(長さが0となる近傍)でのstd::counted_iterator
の振る舞いを変更することでこの問題に対処します。std::counted_iterator
の内部カウンタが0になったときに内部イテレータをインクリメントしないようにし、内部カウンタが0->1になるときもデクリメントしないようにするほか、base()
の動作も長さが0の時は保持するイテレータを進めて返すように変更します。
ただし、random_access_iterator
に対しては現在の振る舞いを維持します。なぜなら、random_access_iterator
となるrange
(view
)はその要素の全てがいつでも利用可能な状態にある事を期待できるためです。
この変更はABI破壊を招くため、それを受け入れるかC++20に逆適用するかしない場合、これと同じことをする新しいクラスを追加することを提案しています。
P2407R0 Freestanding Library: Partial Classes
一部の有用な標準ライブラリのクラス型をフリースタンディング処理系で使用可能とする提案。
使用可能にしようとしているのは次のクラスです
std::array
std::string_view
std::variant
std::optional
フリースタンディングライブラリ機能とすることを提案している理由は、これらのクラスがとても有用であるからです。
これらのクラスには例外を投げうる関数(.at()
やstd::get()
など)が含まれていますが、フリースタンディング環境ではそれらを= delete;
としておくことを提案しています。
P2408R0 Ranges views as inputs to non-Ranges algorithms
非Rangeアルゴリズムのイテレータに対する名前付き要件を、イテレータコンセプトで置き換える提案。
C++20で<algorithm>
に追加された、std::ranges
名前空間の下にあるコンセプトで制約されたアルゴリズム群の事をRangeアルゴリズムと呼び、そうで無い従来のものを非Rangeアルゴリズムと呼び分けます。
Rangeアルゴリズムはコンセプトを用いて各種要件が指定されているのに対して、非Rangeアルゴリズムは名前付き要件(Cpp17ForwardIteratorなど)という文書で要件が指定されています。両者はほとんど同じ事を指定していますが微妙に異なり、コンセプトによって定義されるC++20のイテレータとイテレータ要件によって定義されるC++17以前のイテレータは互換性、特に後方互換性がなく、非RangeアルゴリズムでC++20イテレータを使用する事は推奨されません。
この提案は従来の非Rangeアルゴリズムに対する要件もコンセプトを用いて置き換える事で、C++20のイテレータを従来の非Rangeアルゴリズムで使用可能にするものです。その目的は、C++20のview
から取得したイテレータに対してParallel Argorithmを使用可能とすることにあります。
std::vector<int> data = ...; auto v = data | std::views::transform([](int x){ return x * x; }); int sum_of_squares = std::reduce(std::execution::par, begin(v), end(v)); auto idxs = std::views::iota(0, N); std::transform(std::execution::par, begin(idxs), end(idxs), begin(sqrts), [](int x) { return std::sqrt(float(x)); });
問題となるのは、従来のイテレータカテゴリチェック機構(すなわちstd::iterator_traits
)を使用してC++20イテレータを使用した時、つまりはC++17以前のイテレータを使用している場所でC++20イテレータを使用した時、C++20イテレータはC++17互換イテレータとして振る舞うためにC++20のそれとは異なるイテレータカテゴリを返します。それは多くの場合input_iterator
であり、実際にはそれよりも強いカテゴリとして扱うことができるにも関わらず性質が制限されます。
また、forward
以上の強さのC++20イテレータとC++17イテレータの最大の非互換はイテレータi
に対する*i
の結果型(reference
)が参照型でなくても良いかどうかです。C++17以前はreference
は必ず参照型でしたが、C++20以降はそうではなくても(例えばprvalue)構いません。この提案の最大の問題点はこの点です。
非Rangeアルゴリズムに指定されている要件をコンセプトを用いて書き換える事は、これらの問題に抵触しません。コンセプトを通じた経路ではC++20イテレータは正しくC++20イテレータとして扱われ、C++17以前は間接参照の結果が必ず参照型なのでイテレータコンセプトにおいてそこは問題となりません。そして、イテレータを入力にしか使用しないアルゴリズムはreference
型に依存せずに書くことが(あるいは書き直すことが)できるはずです。
なおこの変更はCpp17ForawardIterator以上の要件に対してのみ行われます。したがって、現在Cpp17InputIteratorを求めているところではC++20イテレータを使用できません。これは、input_iterator
のC++17とC++20間の非互換が他のカテゴリに比べて大きいためです。
<iterator>
- cpprefjp- [C++] C++20からのiterator_traits事情 - 地面を見下ろす少年の足蹴にされる私
- [C++] C++17イテレータ <=> C++20イテレータ != 0 - 地面を見下ろす少年の足蹴にされる私
- P2408 進行状況
P2409R0 Requirements for Usage of C++ Modules at Bloomberg
ブルームバーグ社内における経験から、モジュールの実装およびそれを利用するために必要な要件についてまとめた報告書。
ブルームバーグ社内には数万の独立したC++リポジトリが存在しており、それらのプロジェクトは積極的な依存関係の再構築を行うパッケージマネージャのアプローチによって統合され、ディストリビューションスナップショットと呼ばれるものによって全てのプロジェクトの一貫性が保たれています。ディストリビューションスナップショットはビルド済みの成果物が含まれており、変更が必要となるときは変更されるソースコードによる最小のビルドコンテキストを構成した上で、成果物をディストリビューションスナップショットに対して更新します。
ブルームバーグ社内のこのような経験は一般のオープソースプロジェクトと大きく変わるものではないため、この文書の内容はブルームバーグ社内の経験に大きく影響されるもののブルームバーグに特化したものではありません。
その上で、同様のプロジェクト構造を持つ組織において、モジュールを使用していくために求められる次のような要件を報告しています。
- システムに存在するモジュール数に関係なく、C++ソースコードを含むファイルを開くことなく、一定のI/Oコストで現在のビルドの外側に存在しているモジュールの存在がテスト可能でなければならない
- 例えば、モジュール名から決定される特定のファイルの存在をチェックするなど
- システムに存在するモジュール数に関係なく、一定のI/Oコストで現在のビルドの外側に存在しているモジュールを利用する方法を発見することができる
- 例えば、モジュール名から決定される特定のファイルを読み込むことなど
- モジュールソースコードをパースすることなく、現在のビルドの外側に存在しているモジュールの依存関係を把握することが可能である
- 例えば、モジュール名から決定される特定の依存関係記述ファイルを読み込むことなど
- モジュールの検出機能は、同じプラットフォームで動作するコンパイラや静的解析ツールにの間で相互運用可能である
- モジュールの検出機能には、モジュールのインターフェースをパースするための十分な指示が含まれている
- コンパイルコマンドは、ディスク上のファイルが相互運用可能な方法で発見・パース可能であることに加えて、翻訳単位のセマンティクスを正しく再現するのに十分でなくてはならない
- モジュールの検出機能には、ビルドシステムの外側でモジュールファイルをパースするコストを削減するための、相互運用可能なフォーマットが含まれている
P2410R0 Type-and-resource safety in modern C++
C++CoreGuidelineをベースとして、完全なタイプ&リソースセーフなC++プログラミングのためのルールの概要。
これは提案文書ではなく、タイプ&リソースセーフなC++を書くために意識すべき原則や注意すること、またそれを促進する静的解析の重要性を説明する文書です。
P2411R0 Thoughts on pattern matching
C++へのパターンマッチング機能についてのビャーネ先生の所感。
パターンマッチングに否定的なわけではなく、機能の方向性についてどのようなものが望ましいのかの考えをまとめた文書です。
- 構造化束縛の延長線上にあること
- 構文は統一的かつジェネリックで、宣言的なものであること
- パターンマッチングを、既存のライブラリコードを用いたマッピングで定義することは避けるべき
- 非常に稀な特殊ケースに過度に対応させる必要はない
- パフォーマンスを損ねるものであってはならない
パターンマッチングはC++のコードをよりクリーンかつシンプルに書けるようにすることができる機能を提供できる貴重な機会ですが、その導入に失敗してしまえばC++の複雑さを増大させユーザー離れを招きかねないためその設計は慎重になるべき、という事を言っています。
ビャーネ先生はP2392のis, as
によるパターンマッチングの方向性を支持しているようですが、それをパターンマッチング外部に一般化するのをC++23に間に合わせようとする必要はないとも述べています。
P2412R0 Minimal module support for the standard library
標準ライブラリモジュールについて最小のサポートをC++23に追加する提案
C++20でモジュールが追加されましたが、標準ライブラリはモジュール化されていません。これは次のような問題を生じています
- モジュールの使用を妨げている
- 標準ライブラリのモジュール(定義したり、実装固有のものなど)を使用するプログラムは、そのモジュールを定義せずに移植できない
- 標準ヘッダのように簡単にモジュールを教えられない
- モジュールを利用した現実的な例の不足
- プラットフォーム固有の、準標準モジュールが登場してしまう
標準ライブラリは優れたスタイルの例であるはずですが、特定の機能を使用するために特定のヘッダをインクルードもしくはインポートする必要があり、どの機能がどのヘッダにあるのか覚える必要があるなど多くの人にとって負担になっています。結果的に、これはライブラリ機能を提供する際のアンチパターンになってしまっています。また、標準ヘッダのインクルードは思わぬコンパイル時間の増大を招きます。
標準ライブラリをモジュールとして提供する事でこれらの問題を解決し、標準ライブラリによってモジュールの定義と利用のモデルを提供することを目指す提案です。
次のようなモジュールを提供する事を提案しています。
std.fundamental
std.core
std.io
std.os
std.concurrency
std.math
std
特に重要なのが最後のstd
モジュールで、これをインポートすれば標準ライブラリのすべての機能にアクセスできます(C互換ヘッダを除く)。このstd
モジュールを少なくとも追加した上で、その他細粒度のモジュールは合意が取れるものだけをC++23に導入していく事を目指しています。
標準ヘッダはエクスポートしてはならない実装詳細を含んでおり、std
モジュールは標準ヘッダユニットの集合体であってはならず、標準で規定されている名前だけがエクスポートされている必要があります。import std;
なグローバル名前空間を汚染すべきではなく、マクロをエクスポートすべきでもありません。
ヘッダユニットは結局機能に対して適切なヘッダをインポートしなければならないという問題を解決せず、マクロをリークし、複雑な依存関係とそれによるコンパイル時間の増大、などの問題を解決できません。ヘッダユニットは移行期のメカニズムとして利用することができますが、それに頼りすぎるとヘッダが抱える問題を引きずり続けるリスクがあります。
提案文書には、MSVCのモジュール実装を使用したヘッダインクルードとモジュールインポートによる標準ライブラリ使用時のコンパイル時間の測定例が記載されています。
#include |
import |
import std; |
全ヘッダのインクルード | 全ヘッダのインポート | |
---|---|---|---|---|---|
<iosteream> |
0.87s | 0.32s | 0.08s | 3.43s | 0.62s |
9ヘッダ | 2.20s | 0.77s | 0.44s | 3.53s | 0.99s |
あくまで1つの例であり、まだ最適化されたものではありません。しかし、この結果は標準ライブラリをモジュールとして提供することのメリットの一つを端的に表しています。
P2413R0 Remove unsafe conversions of unique_ptr
std::unique_ptr
における、危険な暗黙変換を禁止する提案。
この提案のいう危険な変換とは次のようなものです。
#include <memory> struct Base {}; struct Derived : Base {}; int main() { std::unique_ptr<Base> base_ptr = std::make_unique<Derived>(); }
Base
を公開継承しているDerived
のポインタはBase
のポインタに暗黙変換することができますが、Base
には仮想デストラクタがないため、そのままBase
のポインタに対してdelete
を呼ぶと未定義動作となります(派生クラスのデストラクタが呼ばれない)。
この問題はstd::unique_ptr
の変換コンストラクタが適切に制約されていないことから来ており、より詳細にはstd::default_delete
の変換コピーコンストラクタが単にstd::is_convertible_v
のチェックしかしていないことによります。
namespace std { template <class T> struct default_delete { // default_deleteの変換コピーコンストラクタの実装例 template <class U> requires is_convertible_v<U*, T*> default_delete(const default_delete<U>&) noexcept {} /*...*/ }; }
生ポインタ間の変換可能性しかチェックしておらず、その後のoperator()
の呼び出し(delete
)が未定義とならないかどうかを気にしていません。
この提案は、ここにさらに要件を加えることで、冒頭のような危険な変換を禁止するものです。
namespace std { template <class T> struct default_delete { // default_deleteの変換コピーコンストラクタの実装例 template <class U> requires is_convertible_v<U*, T*> && ( is_similar_v<U, T> || has_virtual_destructor_v<T> ) default_delete(const default_delete<U>&) noexcept {} /*...*/ }; }
is_similar_v
は説明専用のメタ関数で、2つの型がsimilarな関係にあるかを調べるものです(similarとは2つの型がCV修飾の違いを除いて等しい、みたいな意味です)。
これによってdefault_delete<T> -> default_delete<U>
への変換は、T* -> U*
への変換が可能でありかつ次のどちらかの場合に可能となります。
T
とU
がsimilarな関係にあるT
が仮想デストラクタを持っている
これによって、冒頭のコードはどちらの条件も満たさないためコンパイルエラーとなります。
この変更によって壊れるコードは元々未定義動作を含んでいたものだけであるはずで、実際libc++をフォークしこの変更を適用した上でLLVMをコンパイルしたところlibc++のテストが1件だけコンパイルエラーとなり、それは冒頭のコードのような未定義動作を含んでいたものでした。
ただし、C++20のDestroying Deleteを用いたコードではこの変更が破壊的となる可能性があります。
#include <memory> #include <new> struct Base { void operator delete(Base* ptr, std::destroying_delete_t); }; struct Derived : Base {}; void Base::operator delete(Base* ptr, std::destroying_delete_t) { ::delete static_cast<Derived*>(ptr); } int main() { std::unique_ptr<Base> base_ptr = std::make_unique<Derived>(); }
このコードでは、Base
のポインタに対するdelete
時にデストラクタが呼び出されず、代わりにユーザー定義delete
の処理において静的にディスパッチされ適切なデストラクタを呼び出しています。
このようなコードは稀でしょうがDestroying Deleteの適正な使用法の一つであるため、解決が必要です。提案では、default_delete
に対するカスタマイゼーションポイントを追加することで、特定の型のペアに対して変換を許可する案を提示しています。
P2414R0 Pointer lifetime-end zap proposed solutions
Pointer lifetime-end zapと呼ばれる問題の解決策の提案。
現在のC/C++の規定では、あるオブジェクトを指すポインタはそのオブジェクトの寿命が尽きた時に無効(あるいは不定)となり、その使用(ポインタ値のロードとストアやキャストやポインタ値の比較)は未定義動作となります。しかし、古くから使用されているアルゴリズム、特に並行アルゴリズムではそのような不正なポインタを使用することに依存しているものが多くあり、そのようなアルゴリズムは未定義動作に陥っています。並行アルゴリズムでは、あるオブジェクトからポインタが取得された後でそのポインタが使用されるまでの間に、別のスレッドによってそのオブジェクトが破棄されていることが起こり得、さらにはそのポインタの指す場所に別のオブジェクトが再配置され、デリファレンスによってそれを読みだしてしまう事すら起こり、それに依存するアルゴリズムも存在しています。
そのような規定はコンパイラあるいは診断ツールによる追加の診断や最適化を可能としますが、今日マルチスレッドハードウェアが一般的になっていることから、そのような最適化(特にリンク時最適化)によって並行アルゴリズムの動作が妨げられると、実行時にバグを見えない形で埋め込むことになり、影響はより深刻になります。特に、そのようなアルゴリズムは既に広く使用されていることから、既存のコードをコンパイルしなおしたときにそのようなバグが埋め込まれる可能性があります。
この様な問題は、ポインタがそれが指すオブジェクトの寿命終了とともに消失(zap)するように見えることから、Pointer lifetime-end zap(あるいは、lifetime-end pointer zap)と呼称されます。
この提案は、Pointer lifetime-end zapを解決するために可能ないくつかの解決策について提案するものです。
提案では、無効(不定)なポインタの使用とゾンビポインタのデリファレンス(一度無効となったポインタの指す位置にオブジェクトが再構築された後の参照)の2つのパートに問題を分割し、それぞれに可能ないくつかの解決策を提示しています。
- 無効(不定)なポインタの使用
- 無効なポインタの使用の許可
- ポインタを単にアドレス値の様な値であるとみなしそれ以上の意味論を与えない
- PointerのProvenanceの概念と衝突する
- 無効なポインタを使用可能であるとマークする
usabel_ptr<T>
の様なラッパ型を使用して、ポインタのProvenanceを断ち切る- 現在の規定を変更しない
- 無効化した後再使用するポインタに予めマークしておく
std::atomic
等の既存のライブラリ機能をポインタで使用するときに特別扱いすることでusabel_ptr<T>
と同様の意味論を与える
- 予めマークされた割り当て/解放操作によって取得されるオブジェクトへのポインタについて、無効化した後の利用を許可する
- コンパイラオプションなどによって、そのようなアロケータへのマーキングはコードの外で行う
- 無効なポインタの使用の許可
- ゾンビポインタのデリファレンス
- ゾンビポインタのデリファレンスの許可
- ポインタを単にアドレス値の様な値であるとみなしそれ以上の意味論を与えない
- 最適化を阻害する可能性があり、標準の変更も伴う
- ゾンビポインタのデリファレンスが可能であるとマークする
- 「無効なポインタ使用」の解決策2に、ゾンビポインタのデリファレンスも許可する
- ゾンビポインタとしてデリファレンスしうるポインタに予めマークしておく
- 「無効なポインタ使用」の解決策3に、ゾンビポインタのデリファレンスも許可する
- 2に比べて既存コードへの影響が小さくなる
- ゾンビポインタとしてデリファレンスしうるポインタに予めマークしておく
- 「無効なポインタ使用」の解決策4に、ゾンビポインタのデリファレンスも許可する
- 3に比べて更に既存コードへの影響が小さくなる
- CAS成功時に期待される値が上書きされるモデル
- 通常のCAS操作では期待される値は上書きされない
- ゾンビポインタを期待値としてCASが成功したときにゾンビポインタのProvenanceを再計算させることで有効化する
- 意味論のみの変更であり、実際には書き換えなどは行われない
- ゾンビポインタのデリファレンスの許可
この提案ではまだこれのどれを選ぶのか、また選ばないかは決まっていません。
P2415R0 What is a view?
view
コンセプトの要件を緩和する提案。
これはstd::generator<T>
がO(1)で破棄可能という要件を満たせない事から議論が始まっています。
view
コンセプトは範囲を所有しない軽量なrange
を定義するコンセプトで、構文的には次のように定義されます。
template<class T> concept view = range<T> && movable<T> && enable_view<T>;
そして、意味論要件として次の3つが要求されています。
T
のムーブ構築/代入は定数時間(O(1))T
のデストラクトは定数時間T
はコピー不可であるか、T
のコピー構築/代入は定数時間
この要件は、view
であるT
が範囲を所有せず必然的に軽量な型であることを要求するものです。
view
コンセプトがアルゴリズム関数のようなrange
を受け取る関数の引数を制約するために使用されることはほばなく(その用途には~_range
コンセプトとフォワーディングリファレンスが用いられる)、view
が構築・代入や破棄が軽量であることを求めるのはrange
アダプタの構築のコストを増大させないためです。
auto rng = v | views::some
| views::operations
| views::here;
この様なrange
アダプタの構築はまだ何もしておらず、将来のループのために準備をしているだけですが、これがv
の要素をコピーしたりするものであると準備するだけのこのコードのコストはかなり大きなものになってしまいます。view
コンセプトの意味論要件は、そのようなことが起きずrange
アダプタの構築がv
の要素に触れることは無いことを保証し、range
アダプタ自身がview
であり入力にview
を受け取ることから、そのことはrange
アダプタのすべてのレイヤで保証されます。
view
コンセプトを満たさない型をview
としてしまうことは未定義動作に繋がりますが、それによって実際に起こることはrange
アダプタの構築時に余分なコストが発生しパフォーマンスが低下することです。
しかし、次の様な型を考えてみるとview
コンセプトの破棄に対する要求は本当にパフォーマンスに配慮したものなのかに疑問が生じます。
struct bad_view2 : view_interface<bad_view2> { std::vector<int> v; bad_view2(std::vector<int> v) : v(std::move(v)) { } // movable, but not copyable bad_view2(bad_view2 const&) = delete; bad_view2(bad_view2&&) = default; bad_view2& operator=(bad_view2 const&) = delete; bad_view2& operator+(bad_view2&&) = default; std::vector<int>::iterator begin() { return v.begin(); } std::vector<int>::iterator end() { return v.end(); } };
このbad_view2
はムーブ構築と代入はO(1)でcopyable
ではなくview
コンセプトの構文要件を満たしていますが、O(1)で破棄可能ではないためview
のモデルではありません。
std::vector<int> get_ints(); auto rng = bad_view2(get_ints()) | views::filter([](int i){ return i > 0; }) | views::transform([](int i){ return i * i; });
このrng
の構築にはstd::vector<int>
のムーブ2回が発生し、std::vector<int>
の破棄は3回行われます。破棄がO(1)とならないのは最後のrng
の破棄時です。
このコードは次のように書くこともできます。
auto ints = get_ints(); // 変数に保持しておく auto rng = ints | views::filter([](int i){ return i > 0; }) | views::transform([](int i){ return i * i; });
この場合は現行のview
コンセプト的にも何の問題もなく、rng
の構築時にもstd::vector<int>
のムーブを伴わないので効率的に思えます。しかし、実際にはstd::vector<T>
の破棄がrng
から中間オブジェクトであるints
に移されただけで破棄のコストが無くなったわけではありません。そして、rng
はints
を所有せず参照しているためダングリングの危険に配慮する必要があり、(ref_view
がポインタで参照するため)ポインタの間接参照のコストがかかります。
これらのデメリットはbad_view2
では問題とならず、全体を見比べてみるとbad_view2
は合法な後者のコードよりもパフォーマンスでも安全性でも勝っています。
この提案では、このbad_view2
の様な型を許可するために、view
コンセプトの破棄に関する意味論要件を次のどちらかように変更する事を提案しています。
M
個の要素を持ったT
のオブジェクトがN
回ムーブされたとき、それらN
個のオブジェクトの破棄はO(N+M)
- ムーブ元の
T
のオブジェクトの破棄は定数時間
この要件によって先程のbad_view2
の様な型やstd::generator<T>
は無事にview
となるようになります。
そしてこれによって可能となる次のような型を追加し、views::all
の効果を書き換えることも提案しています。
template <range R> requires is_object_v<R> && movable<R> class owning_view : public view_interface<owning_view<R>> { R r_; // exposition only public: owning_view() = default; constexpr owning_view(R&& t); owning_view(const owning_view&) = delete; owning_view(owning_view&&) = default; owning_view& operator=(const owning_view&) = delete; owning_view& operator=(owning_view&&) = default; constexpr R& base() & { return r_; } constexpr const R& base() const& { return r_; } constexpr R&& base() && { return std::move(r_); } constexpr const R&& base() const&& { return std::move(r_); } constexpr iterator_t<R> begin() { return ranges::begin(r_); } constexpr iterator_t<const R> begin() const requires range<const R>{ return ranges::begin(r_); } constexpr sentinel_t<R> end() { return ranges::end(r_); } constexpr sentinel_t<const R> end() const requires range<const R> { return ranges::end(r_); } // + overloads for empty, size, data }; template <class R> owning_view(R&&) -> owning_view<R>;
このstd::ranges::owning_view<T>
は要するに先ほどのbad_view2
と同じものです。
このowning_view<T>
を用いて、views::all
が従来std::ranges::subrange
を返していたところを書き換えます。それによって右辺値のview
ではないrange
を安全かつ効果的にviews::all
によってview
化できます。views::all
はほとんどのrange
アダプタで使用されているため、これによるメリットは殆どのrange
アダプタが享受できます。
P2416R0 Presentation of requirements in the standard library
現在の規格書の、要件(requirement)の記述方法を変更する提案。
たとえばコンテナ要件やイテレータ要件など、現在は表と文書で記述されているものを箇条書きリストとその詳細文書のような記述に変更するものです。
意味論の変更は意図されておらず、純粋に文書としての体裁の変更です。
P2417R0 A more constexpr bitset
std::bitset
をconstexpr
対応させる提案。
C++11にて、std::bitset
の一部のコンストラクタとoprator[]
がconstexpr
指定されており、限定的に定数式で使用可能でした。他の部分がconstexpr
指定されていないのは、std::bitest<T>::reference
のデストラクタがトリビアルではなかったための様です(トリビアルとすることもできたがABI破壊を伴うため敬遠されていた)。
C++20において定数式での動的メモリ確保が許可され、同時にconstexpr
デストラクタが許可されました。それによって、::reference
のデストラクタを含めたstd::bitset
の全てのメンバ関数をconstexpr
化する障害はなくなっています。
std::bitest
はビットマスクやビットフラグを表すのに便利なクラスで定数式においても同様であり、定数式でも利用可能にすることで同様のクラスを再発明する必要が無くなるなどの利点があります。
この提案は、std::bitset
とstd::bitest<T>::reference
のすべてのメンバ関数および、std::bitset
の一部の演算子オーバーロード(<<
を除いた残り)にconstexpr
を付加するものです。
なお、この提案の前にも同様の提案(P1251R1)が提出されており、そちらは改訂を待った後でLWGに転送される予定でした。しかし、著者の方のレスポンスが無くなり進行していなかったため、この提案が新しい提案として再提出されました。