文書の一覧
全部で106本あります。
もくじ
- N4955 WG21 2023-06 Admin telecon minutes
- N4957 WG21 February 2023 Minutes of Meeting
- P0260R6 C++ Concurrent Queues
- P0260R7 C++ Concurrent Queues
- P0543R3 Saturation arithmetic
- P0843R7 inplace_vector
- P0843R8 inplace_vector
- P0901R11 Size feedback in operator new
- P1030R6 std::filesystem::path_view
- P1324R1 RE: Yet another approach for constrained declarations
- P1383R2 More constexpr for <cmath> and <complex>
- P1729R2 Text Parsing
- P1928R5 std::simd - Merge data-parallel types from the Parallelism TS 2
- P1928R6 std::simd - Merge data-parallel types from the Parallelism TS 2
- P2169R4 A Nice Placeholder With No Name
- P2407R4 Freestanding Library: Partial Classes
- P2487R1 Is attribute-like syntax adequate for contract annotations?
- P2521R4 Contract support -- Record of SG21 consensus
- P2542R3 views::concat
- P2546R5 Debugging Support
- P2548R6 copyable_function
- P2552R3 On the ignorability of standard attributes
- P2591R4 Concatenation of strings and string views
- P2630R4 Submdspan
- P2637R3 Member visit
- P2641R4 Checking if a union alternative is active
- P2642R3 Padded mdspan layouts
- P2662R2 Pack Indexing
- P2689R2 atomic_accessor
- P2697R1 Interfacing bitset with string_view
- P2714R1 Bind front and back to NTTP callables
- P2717R2 Tool Introspection
- P2727R3 std::iterator_interface
- P2728R4 Unicode in the Library, Part 1: UTF Transcoding
- P2728R5 Unicode in the Library, Part 1: UTF Transcoding
- P2741R3 user-generated static_assert messages
- P2752R3 Static storage for braced initializers
- P2757R3 Type checking format args
- P2767R1 flat_map/flat_set omnibus
- P2776R0 2023-05 Library Evolution Poll Outcomes
- P2779R1 Make basic_string_view's range construction conditionally explicit
- P2781R3 std::constexpr_v
- P2785R0 Relocating prvalues
- P2785R1 Relocating prvalues
- P2785R2 Relocating prvalues
- P2785R3 Relocating prvalues
- P2786R2 Trivial relocatability options
- P2795R0 Correct and incorrect code, and "erroneous behaviour"
- P2795R1 Erroneous behaviour for uninitialized reads
- P2795R2 Erroneous behaviour for uninitialized reads
- P2809R1 Trivial infinite loops are not Undefined Behavior
- P2810R1 is_debugger_present is_replaceable
- P2811R5 Contract-Violation Handlers
- P2811R6 Contract-Violation Handlers
- P2811R7 Contract-Violation Handlers
- P2814R1 Trivial Relocatability --- Comparing P1144 with P2786
- P2819R1 Add tuple protocol to complex
- P2821R2 span.at()
- P2821R3 span.at()
- P2828R2 Copy elision for direct-initialization with a conversion function (Core issue 2327)
- P2834R1 Semantic Stability Across Contract-Checking Build Modes
- P2835R1 Expose std::atomic_ref's object address
- P2836R1 std::basic_const_iterator should follow its underlying type's convertibility
- P2845R1 Formatting of std::filesystem::path
- P2865R1 Remove Deprecated Array Comparisons from C++26
- P2872R1 Remove wstring_convert From C++26
- P2874R1 Mandating Annex D
- P2874R2 Mandating Annex D
- P2877R0 Contract Build Modes and Semantics
- P2878R2 Reference checking
- P2878R3 Reference checking
- P2878R4 Reference checking
- P2885R0 Requirements for a Contracts syntax
- P2898R1 Build System Requirements for Importable Headers
- P2902R0 constexpr 'Parallel' Algorithms
- P2904R0 Removing exception in precedence rule(s) when using member pointer syntax
- P2905R0 Runtime format strings
- P2905R1 Runtime format strings
- P2906R0 Structured bindings for std::extents
- P2910R0 C++ Standard Library Ready Issues to be moved in Varna, Jun. 2023
- P2911R0 Python Bindings with Value-Based Reflection
- P2912R0 Concurrent queues and sender/receivers
- P2915R0 Proposed resolution to CWG1223
- P2917R0 An in-line defaulted destructor should keep the copy- and move-operations
- P2917R1 An in-line defaulted destructor should keep the copy- and move-operations
- P2918R0 Runtime format strings II
- P2918R1 Runtime format strings II
- P2920R0 Library Evolution Leadership's Understanding of the Noexcept Policy History
- P2921R0 Exploring std::expected based API alternatives for buffer_queue
- P2922R0 Core Language Working Group "ready" Issues for the June, 2023 meeting
- P2925R0 inplace_vector - D0843R7 LEWG presentation
- P2926R0 std::simd types should be regular - P2892R0 LEWG presentation
- P2929R0 simd_invoke
- P2930R0 Formatter specializations for the standard library
- P2931R0 WG21 February 2023 Meeting Record of Discussion
- P2937R0 Freestanding: Remove strtok
- P2940R0 switch for Pattern Matching
- P2941R0 Identifiers for Pattern Matching
- P2944R0 Comparisons for reference_wrapper
- P2945R0 Additional format specifiers for time_point
- P2946R0 A flexible solution to the problems of noexcept
- P2947R0 Contracts must avoid disclosing sensitive information
- P2949R0 Slides for P2861R0: Narrow Contracts and noexcept are Inherently Incompatable
- P2950R0 Slides for P2836R1: std::basic_const_iterator should follow its underlying type's convertibility
- P2951R0 Shadowing is good for safety
- P2951R1 Shadowing is good for safety
- おわり
N4955 WG21 2023-06 Admin telecon minutes
2023年6月に行われた、WG21管理者ミーティングの議事録
N4957 WG21 February 2023 Minutes of Meeting
2023年6月に行われた、WG21全体会議の議事録
P0260R6 C++ Concurrent Queues
↓
P0260R7 C++ Concurrent Queues
標準ライブラリに並行キューを追加するための設計を練る提案。
以前の記事を参照
R6での変更は
- ターゲットとなるConcurrency TSのリビジョンなどについて追記
- TSに入れると仮定すると浮かぶ疑問について追記
- 非同期インターフェースを追加
などです。
このリビジョンでの変更は
- LEWGのフィードバックを反映
system_error
からconqueue_errc
を導出する- Rangeコンストラクタの追加
capacity()
の追加- 既存の実装例としてTBBの
concurrent_bounded_queue
を追加 pop()
のAPIに関する議論を別の提案に分離
などです。
R6で追加された非同期インターフェースはsender
を返す次のようなものです
sender auto queue::async_push(T x); sender auto queue::async_pop();
これらは要素をpush/pop
する操作を表すsender
を返し、そのsender
が実行されるまでは実際にpush/pop
は行われません。また、このsender
はキャンセルをサポートしています。
P0543R3 Saturation arithmetic
整数の飽和演算を行うライブラリ機能の提案。
以前の記事を参照
- P0543R1 Saturation arithmetic - [C++]WG21月次提案文書を眺める(2022年05月)
- P0543R2 Saturation arithmetic - [C++]WG21月次提案文書を眺める(2022年09月)
このリビジョンでの変更は、LWGのレビューを受けて、事前条件に違反した場合に定数式ではないという指定に関するコメントをdiv_sat
に追加したことです。
この提案はすでにLWGのレビューを終えており、C++26に向けて次の全体会議で投票にかけられる予定です。
P0843R7 inplace_vector
↓
P0843R8 inplace_vector
静的な最大キャパシティを持ちヒープ領域を使用しないstd::vector
であるinplace_vector
の提案。
以前の記事を参照
- P0843R5 static_vector - [C++]WG21月次提案文書を眺める(2022年08月)
- P0843R6 static_vector - [C++]WG21月次提案文書を眺める(2023年05月)
R7での変更は
- 名前を
static_vector
からinplace_vector
へ変更 try_push_back()
がT*
を返すように変更push_back()
が条件付きでstd::bad_alloc
をスローするように変更value_type
がtrivially-copyableであればinplace_vector
も trivially-copyableとなることを明記inplace_vector
をどのヘッダに配置すべきかについてのLEWGの投票をリクエストpush_back()
が参照を返すように変更
このリビジョンでの変更は
<inplace_vector>
に配置することにLEWGの合意が取れた事を追記- 機能テストマクロの追加
try_push_back()/unchecked_push_back()
を提案する文言に追加- Rangeコンストラクタと代入演算子を追加
reserve()
がcapacity()
を超えた場合に例外をスローするように変更shrink_to_fit()
(なにもしない)を追加insert_range()
を追加- ムーブコンストラクタとデストラクタのトリビアル性について追記(
T
がトリビアルならトリビアルになる) capacity()
を推定できないため推論補助を削除erase()/erase_if()
を追加- 設計選択時のLEWGにおける投票結果を追記
operator==/operator<=>
をHidden friendsに変更<inplace_vector>
のフリースタンディング指定を解除(それに関しては別の提案で議論する)
などです。
P0901R11 Size feedback in operator new
::operator new
が実際に確保したメモリのサイズを知ることができるオーバーロードを追加する提案。
以前の記事を参照
- P0901R7 Size feedback in operator new interface - [C++]WG21月次提案文書を眺める(2020年11月)
- P0901R8 Size feedback in operator new interface - [C++]WG21月次提案文書を眺める(2021年01月)
- P0901R9 Size feedback in operator new interface - [C++]WG21月次提案文書を眺める(2022年05月)
- P0901R10 Size feedback in operator new interface - [C++]WG21月次提案文書を眺める(2022年11月)
このリビジョンでの変更は
new
式のサポートを削除return_size_t
はvoid*
メンバをもつ非テンプレートクラスに変更- この機能が
std::allocator
の詳細やフリー関数として提供するのではなく、::operator new
として提供する必要がある理由について追記 - 実装と導入の経験、および動的確保への影響にかんするレポートを追加
などです。
このリビジョンでの変更によって、new
式から直接return_size_t
を取るオーバーロードを呼び出せなくなりました。
#include <new> int main() { auto [ptr, size] = new(std::return_size) int[5]; // ng auto [ptr, size] = ::operator new(decltype(int[5]), std::return_size); // ok }
new
式はメモリを確保した後その領域に指定された型のオブジェクトを構築し、対応するdelete
式はオブジェクトを破棄した後メモリを開放しますが、それらの仕組みはreturn_size_t
を取るオーバーロードによって得られる余剰サイズの領域にあるものについて感知しません。
メモリの解放は自動で行われますが、オブジェクトの構築と破棄の責任はプログラマにありそれは手動で行わなければならなくなります。
// 以前のリビジョンにおけるnew式の使用例 // メモリの確保 // T[5]の領域にはオブジェクトが構築される auto [p, sz] = new (std::return_size) T[5]; // 余剰領域のオブジェクトを構築 for (int i = 5; i < sz / sizeof(T); i++) { new (p[i]) T; } // 確保した領域全体にあるオブジェクトを使用 for (int i = 0; i < sz / sizeof(T); i++) { p[i].DoStuff(); } // 余剰領域のオブジェクトを破棄 for (int i = 5; i < sz / sizeof(T); i++) { p[i].~T(); } // メモリの解放 // T[5]の領域にあるオブジェクトは破棄される delete[] p;
これは冗長となり間違えやすいため、このリビジョンでnew
式を使用してreturn_size_t
を取る::operator new
を呼び出せなくなりました。
::operator new
を使用する場合はメモリの確保だけが行われ、オブジェクトの構築・破棄は全てプログラマの責任となるため、手動でそれを管理する必要があるにしても領域によって場合分けする必要はありません。
P1030R6 std::filesystem::path_view
パス文字列を所有せず参照するstd::filesystem::path_view
の提案。
以前の記事を参照
- P1030R4
std::filesystem::path_view
- WG21月次提案文書を眺める(2020年12月) - P1030R5
std::filesystem::path_view
- WG21月次提案文書を眺める(2022年09月)
このリビジョンでの変更は
- ロケールを取る全てのオーバーロードを削除
- 一貫していない
ostream
のフォーマッタを修正 .render()
を削除render_zero_terminated()
がフリー関数であるように修正rendered_path()
の生存期間のセマンティクスを明確化- 名前付き要件ではなく
path-view-like
型によってpath_view
と同様の型を受け取るオーバーロードを指定 - LEWGのメモを一部を除いて削除
- 機能テストマクロの更新
render_null_terminated/render_unterminated
から誤った引数を削除render_null_terminated/render_unterminated
の文言を追加- 必要に応じて‘implementation defined’を‘see later normative wording’に置き換え
- 現在
const path&
を取っているオーバーロードを参考にnoexcept
を付加 path(path_view)
とpath_view::operator<<
のセマンティクスを指定path_view_fragment
にハッシュサポートを追加
などです。
P1324R1 RE: Yet another approach for constrained declarations
制約付きのauto
による関数宣言構文において、そのテンプレートパラメータ名を直接導入できる構文の提案。
この提案のモチベーションは以前に紹介したP2677R0と共通なので、以前の記事を参照
P2677ではauto:T
のような構文でテンプレートパラメータ名を導入しようとしていましたが、この提案はvoid sort(Sortable auto& c);
をvoid sort(Sortable S& c);
のように書けるようにすることで、auto
の代わりに直接テンプレートパラメータ名を導入しようとするものです。
namespace present { // 現在の制約付きauto関数テンプレート void f(Sortable auto&& x) { using S = decltype(x); // テンプレートパラメータ名Sを取り出す // use S } } namespace p2677 { // P2677提案の制約付き関数テンプレート void f(Sortable auto:S&& x) // テンプレートパラメータ名Sが導入される { // use S } } namespace p1324 { // この提案による制約付き関数テンプレート void f(Sortable S&& x) // テンプレートパラメータ名Sが導入される { // use S } }
この提案ではさらに、この構文を戻り値型制約や変数宣言に対する制約にまで広げています
// 現在 void f(Sortable auto x); Sortable auto f(); Sortable auto x = f(); template <Sortable auto N>void f(); // この提案 void f(Sortable S x); // 関数引数のテンプレートパラメータ導入 Sortable S f(); // 関数戻り値型のテンプレートパラメータ導入 Sortable S x = f(); // 変数宣言時の型名エイリアス導入 template <Sortable S N> void f(); // NTTP宣言時のテンプレートパラメータ導入
ただし、コンセプト パラメータ名 変数名、のような宣言であるため、変数名を省略するとコンパイルエラーになります。
// Numberはコンセプトとする void f(Number N); // ng、Numberが型名ではない void f(Number auto); // ok、テンプレートパラメータ名も変数名も導入されない void f(Number N __); // 別の提案(P1110)で提案されていたプレースホルダ
この制限によって、通常の関数とジェネリックな関数が混同されることが無くなります
void f(Foo V) // 2id: 常に通常の関数宣言 void f(Foo F V) // 3id: ジェネリック関数(この提案) void f(Foo auto) // 1id + auto: ジェネリック関数
この宣言によって導入されたテンプレートパラメータ名は以降の引数宣言のために使用できるほか、別のコンセプトのために使用することもできます
// Number, Concept, AnotherConceptは何かコンセプトとする // 1つのテンプレートパラメータで2つの引数を宣言する void f(Number N x, N y) { } // 複数のコンセプトでそれぞれ変数を宣言し、テンプレートパラメータ名を別のコンセプトで使用する Concept R f(Number N a, AnotherConcept<R> U b);
P1383R2 More constexpr for <cmath>
and <complex>
<cmath>
と<complex>
の数学関数をconstexpr
にする提案。
以前の記事を参照
このリビジョンでの変更は、ベースとなるワーキングドラフトを更新したことと、<complex>
関連の見落としを追加したことなどです。
この提案は2023年6月の全体会議で承認され、C++26WD入りしています。
P1729R2 Text Parsing
std::format
の対となるテキストスキャン機能の提案。
C++20でstd::format
が追加されたことで、いくつか問題を抱えていた従来のテキスト書式付き出力機能であるstd::ostream
やprintf
等に代わるものをC++は手に入れました。しかし、テキスト入力面では改善はなく、同様に問題を抱えている従来のstd::istream
やscanf
等を利用するしかありません。std::format
の対となるものを欠いているということでもあり、このことは標準ライブラリの一貫性を損ねてもいます。
この提案は、std::format
の対となる書式付きテキスト入力機能std::scan
を提供し、その欠けている部分を補おうとするものです。
基本的な使用法
if (auto result = std::scan<std::string, int>("answer = 42", "{} = {}")) { // ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~ // output types input format // string const auto& [key, value] = result->values(); // ~~~~~~~~~~ // 読み取られた値 // result == true // result.begin() points to the end of the given input (the null terminator) // key == "answer" // value == 42 } else { // エラーが起きた場合 // result.error()からエラー情報を取得できる }
基本的には、std::scan<Types...>(input, format)
のようにして、入力文字列input
に対するフォーマット文字列をformat
、型指定をTypes
に渡して使用します。フォーマット文字列の構文はほぼstd::format
のものと共通しており、入力文字列とフォーマット文字列をマッチして、フォーマット文字列中の置換フィールド{}
のある位置に対応する入力文字列中の文字列をTypes
の対応する位置にある型の値として読み取ります。
auto input = "25 54.32E-1 Thompson 56789 0123"; auto result = std::scan<int, float, string_view, int, float, int>( input, "{:d}{:f}{:9}{:2i}{:g}{:o}"); // resultはstd::expected、 operator->は失敗時に例外をスローする auto [i, x, str, j, y, k] = result->values(); // i == 25 // x == 54.32e-1 // str == "Thompson" // j == 56 // y == 789.0 // k == 0123
std::scan<Types...>(...)
の結果はstd::expected
で返されており、成功時はstd::scan_result
という型の値に対して.value()
関数を呼ぶことでスキャン結果をstd::tuple<Types...>
オブジェクトとして得ることができます。
スキャン対象の入力は文字列に限らず、スキャン可能な範囲をとることができます。この要件はscannable_range
コンセプトで表現されています。
// scannable_rangeの定義例 template <class Range, class CharT> concept scannable_range = ranges::forward_range<Range> && same_as<ranges::range_value_t<Range>, CharT>;
forward_range
でありその要素型がCharT
(文字型)であるような範囲であれば読み取ることができ、文字列の範囲となっている多くのものを対象にしています。
// views::reverseからの読み取り例 std::string input{"123 456"}; if (auto result = std::scan<int>(std::views::reverse(input), "{}")) { // 読み取り対象の値が1つなら、result->value()はそのオブジェクトを直接返す // result->value() == 654 }
std::scan
においてはscannable_range
に与えられるCharT
はフォーマット文字列の文字型から取得されるため、入力文字列とフォーマット文字列の文字型は一致している必要があります。
std::scan<int>("42", "{}"); // OK std::scan<int>(L"42", L"{}"); // OK std::scan<int>(L"42", "{}"); // Error: wchar_t[N] is not a scannable_range<char>
エラー時はscan_error
という専用のエラー型(列挙型ではない)の値が得られ、.code()
からエラーコードが取得できるほか.msg()
からエラーメッセージを取得することができます。
if (auto result = std::scan<std::string, int>("answer = 42", "{} = {}")) { ... } else { // エラーが起きた場合 auto err = result.error(); auto ec = err.code(); // エラーコードの取得 std::println("Error! : {:s}", err.msg()); // エラーメッセージの出力 }
フォーマット文字列はstd::format
にほとんど準じていますが、数値型に対する次の一部のオプションは無効化されます
これらのオプションは読み取り時には意味がなく、スキャン時は全ての可能性を考慮しどれかの形式をデフォルトにしたり無効化することを回避しています。
そのほかのオプションは利用可能となりますが、出力ではなく読み取りに使用するものであるためstd::format
からその意味が少し変わっています。
また、std::scan
でのみ利用できるオプションも追加されています
i
: プリフィックスから基数を検出する(デフォルトは10進数)- 整数型のみ
u
:-
を考慮しない10進整数値- 整数型のみ
c
: 入力文字(列)をそのままコピーする- 文字列型/文字型/数値型で有効
std::formatter
と同様にstd::scanner
クラステンプレートを特殊化することで、ユーザー定義型をスキャン可能な型として登録することができます。
// tmのスキャンを有効化する際の宣言の例 template <> struct std::scanner<tm, char> { constexpr auto parse(scan_parse_context& ctx) -> expected<scan_parse_context::iterator, scan_error>; template <class ScanContext> auto scan(tm& t, ScanContext& ctx) const -> expected<typename ScanContext::iterator, scan_error>; };
ロケールはデフォルトでは考慮せず、L
オプションとともにstd::locale
オブジェクトを渡すことでロケール依存の読み取りを行うことができます。その際、ロケールオブジェクトは引数の先頭で渡します。
この提案の内容は、scnlib(特にdevブランチ)および{fmt}
ライブラリにおいて試験実装されているようです。
P1928R5 std::simd - Merge data-parallel types from the Parallelism TS 2
↓
P1928R6 std::simd - Merge data-parallel types from the Parallelism TS 2
std::simd<T>
をParallelism TS v2から標準ライブラリへ移す提案。
以前の記事を参照
- P1928R1 Merge data-parallel types from the Parallelism TS 2 - WG21月次提案文書を眺める(2022年10月)
- P1928R2 Merge data-parallel types from the Parallelism TS 2 - WG21月次提案文書を眺める(2023年01月)
- P1928R3 Merge data-parallel types from the Parallelism TS 2 - WG21月次提案文書を眺める(2023年02月)
- P1928R4
std::simd
- Merge data-parallel types from the Parallelism TS 2 - WG21月次提案文書を眺める(2023年05月)
R5での変更はsimd_select
に関する議論を追記した事です。
このリビジョンでの変更は
- LEWGのレビューを受けての文言の修正
simd_cat
とsimd_split
をリネームsimd_cat(array)
オーバーロードを削除simd_split
をP1928R4で提案されているように修正output_iterator
の代わりにindirectly_writable
を使用- ほとんどの
size_t, int
の使用をsimd-size-type
(符号付整数型)に置き換え simd_abi
に関するものをすべて削除- 説明専用のABIタグを用いてABIタグのセクションを置き換え
- ジェネレータコンストラクタは、インデックスにつき1度だけcallableを呼び出すことを保証
- ブロードキャスティングコンストラクタの変換規則から、
int/unsigned int
を例外としているのを削除 loadstore_flags
をsimd_flags
にリネームsimd_flags::operator|
をconsteval
に変更- 最小SIMD幅を64に増加
hmin/hmax
をreduce_min/reduce_max
にリネームsimd_mask<T, Abi>
をbasic_simd_mask<Bytes, Abi>
にリファクタリングし、それに応じ使用箇所を置き換えsimd<T, Abi>
をbasic_simd<Bytes, Abi>
にリファクタリングし、それに応じ使用箇所を置き換え- ベクトル化可能な型から
long double
を削除 is_abi_tag, is_simd, is_simd_mask
を削除simd_size
を説明専用にした
などです。
この提案はLEWGでのレビューをいったん終えて、LWGに転送するための投票待ちをしています。
P2169R4 A Nice Placeholder With No Name
宣言以降使用されず追加情報を提供するための名前をつける必要もない変数を表すために_
を言語サポート付きで使用できる様にする提案。
以前の記事を参照
- P2169R0 A Nice Placeholder With No Name - WG21月次提案文書を眺める(2020年5月)
- P2169R1 A Nice Placeholder With No Name - WG21月次提案文書を眺める(2022年7月)
- P2169R2 A Nice Placeholder With No Name - WG21月次提案文書を眺める(2022年9月)
- P2169R3 A Nice Placeholder With No Name - WG21月次提案文書を眺める(2023年01月)
このリビジョンでの変更は、提案する文言の改善のみです。
この提案は6月の全体会議で投票にかけられ、C++26WDに導入されています。
P2407R4 Freestanding Library: Partial Classes
一部の有用な標準ライブラリのクラス型をフリースタンディング処理系で使用可能とする提案。
以前の記事を参照
- P2407R0 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2021年07月)
- P2407R1 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2021年11月)
- P2407R2 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2023年01月)
- P2407R3 Freestanding Library: Partial Classes - WG21月次提案文書を眺める(2023年04月)
このリビジョンでの変更は
<algorithm>
からfill_n
とswap_ranges
をフリースタンディングとして追加optional
において、.value()
の使用を**this
で置き換えvariant
において、get
の使用をget_if
で置き換えstring_view
のfind
および検索系関数において、at()
の使用をdata_
で置き換え- 機能テストマクロへの
// freestanding
を追記 - "synopsis"と"header synopsis"の使い分けをより慎重に判断
freestanding-deleted
とfreestanding-partial
は要件が変更され、それぞれ異なるフリースタンディング指定となった// hosted
と// freestanding-deleted
のオーバーロード解決への影響の違いについて追記- フリースタンディング機能をより明確化
などです。
この提案は既にLWGのレビューを終え、次の全体会議で投票にかけられる予定です。
P2487R1 Is attribute-like syntax adequate for contract annotations?
契約プログラミングの構文について、属性likeな構文は契約の指定に適しているかを考察する文書。
以前の記事を参照
このリビジョンでの変更は
- P2552R2および最新のWDに基づいて、属性の無視可能性に関する議論を更新
- 属性の式に関する説明を追記
- P2552R2で提示された意味論の無視可能性基準に関する議論を追加
などです。
P2521R4 Contract support -- Record of SG21 consensus
以前の記事を参照
- P2521R1 Contract support -- Working Paper - WG21月次提案文書を眺める(2022年02月)
- P2521R2 Contract support - Working Paper - WG21月次提案文書を眺める(2022年03月)
- P2521R3 Contract support -- Record of SG21 consensus - WG21月次提案文書を眺める(2023年02月)
このリビジョンでの変更は
- 複数の仮想関数を異なる契約アノテーションでオーバーライドする方法に発見されたバグに関して追記
- ビルドモードは廃止され、各契約条件ごとに何が起こるかは実装定義となった
longjmp
など通常とは異なる方法で終了する契約条件式で何が起こるかを指定- 違反ハンドラのセマンティクスを修正
- 例外を投げる契約条件式の意味論を指定
- トリビアルな関数に対する契約アノテーションに関するイシューを追記
などです。
P2542R3 views::concat
同じ要素型を持つ異なる型の範囲を連結するRangeファクトリ、views::concat
の提案。
- P2542R0
views::concat
- WG21月次提案文書を眺める(2022年02月) - P2542R1
views::concat
- WG21月次提案文書を眺める(2022年04月) - P2542R2
views::concat
- WG21月次提案文書を眺める(2022年05月)
このリビジョンでの変更は
iter_swap
の再設計random_access_range
制約の緩和- 異なる型の変換を修正
- 提案する文言の修正
などです。
P2546R5 Debugging Support
標準ライブラリにデバッグサポートの為のユーティリティを追加する提案。
以前の記事を参照
- P2546R0 Debugging Support - WG21月次提案文書を眺める(2022年02月)
- P2546R1 Debugging Support - WG21月次提案文書を眺める(2022年04月)
- P2546R2 Debugging Support - WG21月次提案文書を眺める(2022年10月)
- P2546R3 Debugging Support - WG21月次提案文書を眺める(2022年11月)
- P2546R4 Debugging Support - WG21月次提案文書を眺める(2023年04月)
このリビジョンでの変更は、LWGのレビューに伴う文言の修正と投票結果を追記した事です。
この提案は既にLWGのレビューを終えて次の全体会議で投票にかけられる予定です。
P2548R6 copyable_function
std::move_only_function
に対して、コピー可能なCallableラッパであるcopyable_function
の提案。
以前の記事を参照
- P2548R0
copyable_function
- WG21月次提案文書を眺める(2022年07月) - P2548R1
copyable_function
- WG21月次提案文書を眺める(2022年10月) - P2548R4
copyable_function
- WG21月次提案文書を眺める(2022年11月) - P2548R5
copyable_function
- WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- 型消去Callableラッパの2重ラッピングに関する文言を追加
- 提案する文言の修正
などです。
この提案は2023年6月の全体会議でC++26に採択されています。
P2552R3 On the ignorability of standard attributes
属性を無視できるという概念について、定義し直す提案。
以前の記事を参照
- P2552R0 On the ignorability of standard attributes - WG21月次提案文書を眺める(2022年02月)
- P2552R1 On the ignorability of standard attributes - WG21月次提案文書を眺める(2022年11月)
- P2552R2 On the ignorability of standard attributes - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- CWG/EWGのフィードバックを反映して、
__has_cpp_attribute
の文言を修正 - 理論的根拠として
carries_dependency
に関するセクションを追加
などです。
この提案は2023年6月の全体会議でC++26に採択されています。
P2591R4 Concatenation of strings and string views
std::string
とstd::string_view
を+
で結合できるようにする提案。
以前の記事を参照
- P2591R0 Concatenation of strings and string views - WG21月次提案文書を眺める(2022年05月)
- P2591R1 Concatenation of strings and string views - WG21月次提案文書を眺める(2022年06月)
- P2591R2 Concatenation of strings and string views - WG21月次提案文書を眺める(2023年01月)
- P2591R3 Concatenation of strings and string views - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更
- LEWGのフィードバックを反映
- Hidden friendsの導入する非対称性を回避するために、フリー関数テンプレートに戻した
- 実装中のプロトタイプに要求されたテスト(
filesystem::path
および曖昧さを導入するテスト)を追加 - 最新のWDに追随
などです。
この提案はLEWGのレビューを終えてLWGに転送するための投票待ちをしています。
P2630R4 Submdspan
std::mdspan
の部分スライスを取得する関数submdspan()
の提案。
以前の記事を参照
- P2630R0 Submdspan - WG21月次提案文書を眺める(2022年08月)
- P2630R1 Submdspan - WG21月次提案文書を眺める(2022年10月)
- P2630R2 Submdspan - WG21月次提案文書を眺める(2023年01月)
- P2630R3 Submdspan - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- LWGのフィードバックを反映
submdspan_mapping
をHidden friendsに変更submdspan_mapping
がADL経由で呼び出されることを規定- 説明専用の実装詳細関数をHidden frinedsとした
is-strided-slice
を削除し、strided_slice
の特殊化であるS_k
に置き換え[[no_unique_address]]
とデフォルト初期化をstrided_slice
とsubmdspan_mapping_result
メンバに追加first_, last_
のテンプレートパラメータにk
を追加- LWGのフィードバックを反映
などです。
この提案は2023年6月の全体会議でC++26に採択されています。
P2637R3 Member visit
std::visit
やなどをメンバ関数として追加する提案。
以前の記事を参照
- P2637R0 Member
visit
andapply
- WG21月次提案文書を眺める(2022年09月) - P2637R1 Member
visit
- WG21月次提案文書を眺める(2022年10月) - P2637R2 Member
visit
- WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は、文言の修正と機能テストマクロに関するメモを追記した事です。
この提案は2023年6月の全体会議でC++26に向けて採択されています。
P2641R4 Checking if a union alternative is active
定数式において、あるオブジェクトが生存期間内にあるかを調べるためのstd::is_within_lifetime()
の提案。
以前の記事を参照
- P2641R0 Checking if a union alternative is active - WG21月次提案文書を眺める(2022年09月)
- P2641R2 Checking if a union alternative is active - WG21月次提案文書を眺める(2022年10月)
- P2641R3 Checking if a union alternative is active - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は、提案する文言の改善のみです。
この提案は2023年6月の全体会議でC++26に向けて採択されています。
P2642R3 Padded mdspan layouts
std::mdspan
でpadding strideをサポートするためのレイアウト指定クラスを追加する提案。
以前の記事を参照
- P2642R0 Padded mdspan layouts - WG21月次提案文書を眺める(2022年09月)
- P2642R1 Padded mdspan layouts - WG21月次提案文書を眺める(2022年10月)
- P2642R2 Padded mdspan layouts - WG21月次提案文書を眺める(2023年01月)
このリビジョンでの変更は
- P2630R3(
submdspan
)の変更に追随 - P2897(
aligned_accessor
)のリファレンスを追加 - 既存のレイアウトマッピング型に合わせて、
.extents()
の戻り値型をextents_type
からconst extents_type&
へ変更 - LEWGの投票結果を受けた設計に関する議論を追加
- rank 1のpadded layoutの
required_span_size()
の設計に関する議論を追加 - 実装経験リンクの更新
layout_{left,right}_padded
のマッピングクラスからの変換コンストラクタと、layout_{left,right}_padded
のマッピングクラスとのoperator==
を- 共著者の追加
などです。
P2662R2 Pack Indexing
以前の記事を参照
パラメータパックにインデックスアクセスできるようにする提案。
以前の記事を参照
- P2662R0 Pack Indexing - [C++]WG21月次提案文書を眺める(2022年10月)
- P2662R1 Pack Indexing - [C++]WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- CWGレビューを受けての文言の改善
index-type-specifier
から型を推論する方法に関するセクションを追加- 将来的な機能拡張に関するセクションを拡充
などです。
P2689R2 atomic_accessor
アトミック操作を適用した参照を返すmdspan
のアクセッサである、atomic_accessor
の提案。
以前の記事を参照
- P2689R0
atomic_accessor
- WG21月次提案文書を眺める(2022年10月) - P2689R1
atomic_accessor
- WG21月次提案文書を眺める(2023年01月)
このリビジョンでの変更は
atomic-ref-bounded
をatomic-ref-bound
にリネームatomic-ref-unbounded
をatomic-ref-unbound
にリネーム- 提案する文言の修正
- P2616が採択された場合、同様の変更をこちらにも適用する必要があることについて追記
などです。
P2697R1 Interfacing bitset with string_view
std::bitset
にstd::string_view
を受け取るコンストラクタを追加する提案。
以前の記事を参照
このリビジョンでの変更は、LWGのレビューを受けての提案する文言の修正です。
この提案は2023年6月の全体会議でC++26に向けて採択されています。
P2714R1 Bind front and back to NTTP callables
std::bind_front
とstd::bind_back
にNTTPとして呼び出し可能なものを渡すオーバーロードを追加する提案。
以前の記事を参照
このリビジョンでの変更は、提案する文言の改善です。
この提案は2023年6月の全体会議でC++26に向けて採択されています。
P2717R2 Tool Introspection
C++周辺ツールが、Ecosystem ISにどれほど準拠しているのかを互いに通信する手段を標準化する提案。
以前の記事を参照
- P2717R0 Tool Introspection - WG21月次提案文書を眺める(2023年01月)
- P2717R1 Tool Introspection - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- Ecosystem ISの最新の変更に追随
- 必要に応じてリテラルにユニコードを使用
- セマンティックバージョニングとJSON、及び先行0を許可しない仕様に従うようにバージョン番号を修正
- フルレベルサポートにバージョン範囲の配列を追加し、サポートの不一致を報告できるようにした
- JSONスキーマの修正
- capability名で数字を使用できるように修正
- コマンドオプションの処理と使用ファイルに関する説明を追加
- 問い合わせのために使用するユーザーインタフェースに関する選択肢を追加し、現在推奨されている選択肢について説明
などです。
P2727R3 std::iterator_interface
イテレータを簡単に書くためのヘルパクラスの提案。
以前の記事を参照
- P2727R0
std::iterator_interface
- WG21月次提案文書を眺める(2022年11月) - P2727R1
std::iterator_interface
- WG21月次提案文書を眺める(2023年02月) - P2727R2
std::iterator_interface
- WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- CRTPの代わりにdeducing thisを使用
- 以前にHidden friendsだったものをそうではなくした
などです。
P2728R4 Unicode in the Library, Part 1: UTF Transcoding
↓
P2728R5 Unicode in the Library, Part 1: UTF Transcoding
標準ライブラリにユニコード文字列の相互変換サポートを追加する提案。
以前の記事を参照
- P2728R0 Unicode in the Library, Part 1: UTF Transcoding - WG21月次提案文書を眺める(2023年01月)
- P2728R3 Unicode in the Library, Part 1: UTF Transcoding - WG21月次提案文書を眺める(2023年05月)
R4での変更は
code_unit
コンセプトの定義を変更し、as_charN_t
アダプタを追加replacement_character
を除くユーティリティとユニコード関連の定数を削除utf_iterator
の制約をわずかに変更null_sentinel_t
をユニコード固有のものに戻した
このリビジョンでの変更は
unpacking_owning_view
をunpacking_view
に置き換え- アダプタでアンパッキングを行うのではなく、アンパッキングを行うために使用する
- 提案するすべての
view
のbegin/end
にconst
/非const
オーバーロードを追加 null_sentinel_t
をstd
名前空間に移動し、その.base()
を削除。単なるポインタ以上の用途に使用できるようにした
などです。
P2741R3 user-generated static_assert messages
static_assert
の診断メッセージ(第二引数)に、コンパイル時に生成した文字列を指定できるようにする提案。
以前の記事を参照
- P2741R0 user-generated
static_assert
messages - WG21月次提案文書を眺める(2023年01月) - P2741R1 user-generated
static_assert
messages - WG21月次提案文書を眺める(2023年02月) - P2741R2 user-generated
static_assert
messages - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は、設計セクションを拡張してインスタンス化と再帰がどのように機能するかについて追記したことです。
P2752R3 Static storage for braced initializers
std::initializer_list
の暗黙の配列がスタックではなく静的ストレージに配置されるようにする提案。
以前の記事を参照
- P2752R0 Static storage for braced initializers - WG21月次提案文書を眺める(2023年01月)
- P2752R1 Static storage for braced initializers - WG21月次提案文書を眺める(2023年04月)
- P2752R2 Static storage for braced initializers - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- この提案は記憶域期間を変更しないことを確認するEWGの投票の結果を受けて、Annex Cにそれを追記
mutable
メンバの説明を追記- 例を追加
- 「§5.1 However...」セクションを追加
この提案は2023年6月の全体会議でC++26に向けて承認されています。
P2757R3 Type checking format args
std::format()
のフォーマット文字列構文について、幅/精度の動的な指定時の型の検証をコンパイル時に行うようにする提案。
以前の記事を参照
- P2757R0 Type checking format args - WG21月次提案文書を眺める(2023年01月)
- P2757R1 Type checking format args - WG21月次提案文書を眺める(2023年04月)
- P2757R2 Type checking format args - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は、提案する文言の改善と機能テストマクロについて注記を追加したことです。
この提案は2023年6月の全体会議でC++26に向けて承認されています。
P2767R1 flat_map/flat_set
omnibus
flat_map
/flat_set
の仕様にあるいくつかの問題点とその解決策について報告する提案。
以前の記事を参照
このリビジョンでの変更は
- アロケータを受け取るコンストラクタの調整については編集上の修正として受け入れられたため提案から分離された
- LWGのレビュー受けての提案する文言の更新と、関連する根拠の追記
- LEWGのレビューが必要なため、Heterogeneous
insert
に関して§7の残りのものと分離(§13へ) - 注意を引くために非
explicit
コンテナコンストラクタに関してを§12から分離- これは2つのコンテナからの非
explicit
コンテナコンストラクタとして§14へ移動
- これは2つのコンテナからの非
などです。
P2776R0 2023-05 Library Evolution Poll Outcomes
2023年5月に行われたLEWGの全体投票の結果を報告する文書。
次の13本の提案が投票にかけられ、否決されたものはありませんでした。
- P1673R12 BLAS Linear Algebra
- P2630R3 submdspan
- P1383R1 More constexpr For
And - P2546R3 Debugging Support
- P2548R5 copyable_function
- P2714R0 Bind Front And Back To NTTP Callables
- P2637R2 Member visi
- P2757R2 Type-Checking Format Args
- P2641R3 Checking If A Union Alternative Is Active
- P1759R6 Native Handles And File Streams
- P2697R0 Interfacing bitset With string_view
- P2248R7 Enabling List-Initialization For Algorithms
- P2734R0 Adding The New 2022 SI Prefixe
全て、C++26に向けてLWGに転送するための投票です。これらのうちのいくつかは、2023年6月の全体会議でC++26 WDに導入されています。
P2779R1 Make basic_string_view's range construction conditionally explicit
std::string_view
のrange
コンストラクタのexplicit
を条件付きに緩和する提案。
以前の記事を参照
このリビジョンでの変更は
- ベースとなるドラフトの更新
- オプション1で提案する型特性の名前を変更し、2つのサブオプションに分割
などです。
分割された代替案は、変換しようとしているstring_view
-likeな型の内部traits
の(std::char_traits
との)互換性をチェックを行うかどうかによる、次の2つです
- 内部
traits
の互換性を無視する - 内部
traits
の互換性を考慮するものとしないものの2つを用意する
(提案の現在の記述では、2つ目のサブオプションがどう使用されるのかわかりませんでした・・・)
SG16でのレビューではこの提案の問題としているところの解決に積極的な賛同は得られなかったようですが、解決するのであれば明示的なオプトイン(コンセプトによるチェックによる自動判定ではなく)によるものが望ましいというコンセンサスは得られています。この提案を進めるかどうかはLEWGに委ねられています。
P2781R3 std::constexpr_v
コンパイル時定数オブジェクトを生成するクラスの提案。
以前の記事を参照
このリビジョンでの変更は
- 後置戻り値型の不必要な使用の修正
- 代替トークン(
and or not
)を使用しないように修正 constexpr-param
(説明専用)コンセプトの要件の修正constexpr_v
のテンプレートパラメータT
があることによって与えられるADLサポートの不完全な側面について追記
などです。
P2785R0 Relocating prvalues
↓
P2785R1 Relocating prvalues
↓
P2785R2 Relocating prvalues
↓
P2785R3 Relocating prvalues
prvalueからのリロケーションを可能とするための機能を導入する提案。
リロケーションについてと提案のモチベーションは以前の同種提案と共通しているのでそちらを参照
- P1144R6 Object relocation in terms of move plus destroy - WG21月次提案文書を眺める(2022年06月)
- P1144R8
std::is_trivially_relocatable
- WG21月次提案文書を眺める(2023年05月) - P2786R0 Trivial relocatability options - WG21月次提案文書を眺める(2023年02月)
- P2839R0 Nontrivial relocation via a new "owning reference" type - WG21月次提案文書を眺める(2023年05月)
この提案では特に、構築後に動かせなくなるオブジェクトの扱いを改善するためにもリロケーション操作が有効であると述べています。
例えば、gsl::not_null
というクラスはnullptr
状態を取り得るクラスのムーブコンストラクタ等を無効化することでそのクラスがnullptr
ではないことを保証するクラスです。gsl::not_null<std::unique_ptr<T>>
のように使用する場合、この型のオブジェクトはムーブもコピーもできなくなります。
このようなオブジェクトはnullptr
ではないことが保証されていることからコードの正確性やパフォーマンスを向上させるために有効ですが、現在のC++コードでは扱うのが現実的ではありません。コピーもムーブもできないため構築後にメモリ上を移動できなくなり、関数に渡したりコンテナに保存したりすることやクラスのメンバとなることを妨げます。
しかし、リロケーション操作が可能になればそのようなオブジェクトは、そのクラスの不変条件を保ったままリロケーションによってメモリ上を移動することができるようになり、その取り扱いのしやすさが改善します。
同様の問題は定数(const
)オブジェクトにもあります。定数オブジェクトはその生存期間を通じて変化しないため、人間にとっても機械(コンパイラ)にとってもプログラムの状態に関する推論がしやすくなります。そのため、自動変数はさまざまなガイドラインで可能ならconst
とすることが推奨されています。
しかし、const
オブジェクトはムーブすることができず、そのため後でムーブすることを意図するオブジェクトはconst
にすることができません。生存期間中はconst
でありながらも、その終わりに所有するリソースを手放すことができれば、より安全で読みやすいコードが可能になります。
この提案のリロケーション操作はこれらの2点を改善することを主目的としており、その点が以前の提案と大きく異なる部分です。
この提案が導入しようとしているのは次のものです
この提案では、リロケーションのために新しい型を導入することを避け、代わりにprvalue(修飾なしの素の型)をそのために利用しようとしています。
提案より、サンプルコード
void foo(std::string str); auto get_string() -> std::string; auto get_strings() -> std::pair<std::string, std::string>; std::string gStr = "static string"; void bar(void) { std::string str = "test string"; foo(reloc str); // OK: std::stringにリロケーションコンストラクタがあればリロケーションされる foo(reloc gStr); // ill-formed: gStrはローカル変数ではない std::pair p{std::string{}, std::string{}}; foo(reloc p.first); // ill-formed: p.firstは完全なオブジェクトではなく、変数名でもない foo(reloc get_string()); // ill-formed: 変数名ではない foo(reloc get_strings().first); // ill-formed: 完全なオブジェクトではなく、変数名でもない foo(auto(str)); // ill-formed: リロケーション後の変数名は使用できない } void foobar(const std::string& str) { foo(reloc str); // OK: 参照をリロケーションする // strの参照先オブジェクトの生存期間は影響を受けない } void foobar(std::string* str) { foo(reloc *str); // ill-formed: *strは変数名ではない } void foobar2(std::string* str) { foobar(reloc str); // OK, ポインタをリロケーションする // strの参照先オブジェクトの生存期間は影響を受けない } class A { std::string _str; public: void bar() { foo(reloc _str); // ill-formed: _strは完全なオブジェクトではなく、ローカル変数でもない } };
P2786R2 Trivial relocatability options
trivially relocatableをサポートするための提案。
以前の記事を参照
- P2786R0 Trivial relocatability options - WG21月次提案文書を眺める(2023年02月)
- P2786R1 Trivial relocatability options - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は
- P1144への参照を最新のもの(5月公開分)に更新
- 用語と定義の明確化のためにセクションを追加
- 新しい構文について欠けていた例を追加
- 基礎的ではない関数をライブラリ関数とした
などです。
この提案はP1144とともにEWGIでレビューされ、今後も議論を続けていくことに合意がとれています。また、この提案単体で既にEWGにおけるレビュー中です(とはいえP1144と強調して、より論点を明確にすることなどが求められています)。
P2795R0 Correct and incorrect code, and "erroneous behaviour"
↓
P2795R1 Erroneous behaviour for uninitialized reads
↓
P2795R2 Erroneous behaviour for uninitialized reads
未初期化変数の読み取りに関して、Erroneous Behaviourという振る舞いの規定を追加する提案。
未初期化変数の読み取りに関するErroneous BehaviourとはP2754R0で導入された概念で、未初期化変数を実装定義の値に初期化した上でその値の(ユーザーが書き込む前の)読み取りに対して指定される標準で定義された動作の1つです。
P2754R0については以前の記事を参照
Erroneous Behaviour(誤った動作、EB)は未定義動作ではなく、実装はユーザーによって初期化されていない変数を特定の値で変数を初期化しているためそれが起きたとしても安全であり、初期化する値を調整することでテストやデバッグに役立てることができます。また、EBを未定義動作として扱えば現状維持になり、EB/UBのまま維持しておくことで将来のさらなる大胆な改善(デフォルト初期化仕様の値初期化への完全な置き換えなど)のための門戸を開いておくこともできます。
未初期化変数の読み取りという問題の解決策としてEBの導入は最善(実現可能性、後方互換性、表現力の保護の観点で最善)であると認識されていますが、現在のC++標準ではEBという概念は定義されておらず、その導入そのものがハードルだとされていました。
この提案は、そのErroneous Behaviourという概念を標準に導入し、未初期化変数読み取り問題の解決を図るものです。
この提案では、自動変数のデフォルト初期化を次のように変更することを提案しています
自動変数のデフォルト初期化は、実装によって定義された固定値で変数を初期化する。
実装はこのエラーを診断することが許可されており、また推奨されているが、エラーを無視して読み取りを有効なものとして扱うことも許可されている。
Erroneous Behaviourというワードが直接出現するわけではありませんが、この文章の後半部分(2行目)がそれを意図しています。すなわち、初期化されていない値を読むことは意図したものではなく間違いなくバグではあり修正する必要があるものの、それを含むコードはwell-definedでありその点について診断されないとしてもプログラムは予測可能である(未定義動作ではない)、とするものです。
言い換えると、未初期化の値を読み取ることは誤りではあるものの、実装がそれを止めなければプログラムはその読み取りの結果として(未定義ではない)何らかの特定の値を得ることになります。実装は診断をしてもいいが、してもしなくてもそれについてwell-definedであることを保証する必要があり、誤った振る舞いが実行された場合に未定義動作及びそれに起因する結果(ソースコードからは予測できない命令の実行、タイムトラベルなど)をもたらすことはありません。
extern void f(int); int main() { int x; // default-initialized, value of x is indeterminate f(x); // glvalue-to-prvalue conversion has undefined behaviour }
この現在UBとなるコードについて、現在及びP2723R1(強制ゼロ初期化)とこの提案によるコンパイル結果の違いは次のようになります
動作について | C++23 | P2723R1 | この提案 |
---|---|---|---|
未定義動作? | UB | well-defined | EB |
それはバグ? | 確実にバグ | 意図的に0初期化しているのか忘れているのかわからない | 確実にバグ |
コンパイラの診断は可能か? | コンパイラはこれを拒否することが許可されている | 適合するコンパイラは診断できない | 適合するコンパイラは受け入れなければならないが、QoIによって拒否することが許可されている |
この提案の下での動作においては、x
の値は何か特定の値に初期化されていますがその値は必ずしも0ではありません。これは、デバッグやテストに役立てるために特定の値を設定することを許可することと、特定の固定値にプログラマが依存してしまうことを回避することを意図しています。
この提案による標準の他の部分への影響は次のようなものが想定されます
- 自動変数は全て何かしら初期化されるため、パフォーマンスに影響を与える可能性がある
- このコストに関する分析はP2723R1でなされている
- また、このコストは非クラス型だけではなく、パディングを持たずデフォルトコンストラクタが全てのメンバを初期化するようなクラス型にもかかってくる
- 特に、共用体は完全に初期化される
- 一般に、共用体をコピーしても誤りではなくパディングビットをコピーしても誤りではない
- これは、誤りのある値のglvalueからprvalueへの変換自体は誤りではないものの、その値をコピー以外のことに使用するのは誤りであることを意味する
- この提案は初期化のセマンティクスのみに影響し、不定値の使用の全般に影響するわけではない
- 例えば、初期化された変数に不定値をコピーすることができ、その値を読み取ると未定義動作となる可能性がある
- この提案は単一操作としての自動変数のデフォルト初期化にのみ影響する
- 自動変数をplacement newでデフォルト初期化するような場合はこの提案の保証はない
この提案の実装に関しては、ほぼ同様のことを行うことがgcc/clangにおいてftrivial-auto-var-init=zero
というオプションによって利用可能となっています(ただしこれは診断を意図したものではないようです)。この提案の変更は主に標準内の動作仕様に関する変更であり、実装の負担は軽微だと思われます。
この提案の採択によって未初期化変数読み取りに関する未定義動作が誤った動作に変更される場合、コードベースに対する影響は次のようになるでしょう
- 今日の正しいコード : パフォーマンスが低下する可能性がある他は観測可能な変化はない
- 今日の正しくないコード : UBをEBに変更した場合(診断しない場合)、そのコードは依然として正しくはないもののその振る舞いは未定義動作ではなくなり、特定の振る舞いするようになる
また、EBはこの提案では未初期化変数の読み取りのために導入しようとしていますが、同様に現在未定義動作となっているもののバグと意図的なものを弁別でき、バグについてのみ診断が可能な未定義動作についてもこれと同様にEBとして指定することで未定義動作やそれに伴う意図しない動作を回避しながら安全な動作をさせるようにすることができる可能性があり、その候補リストも示されています。
- P2723R1 Zero-initialize objects of automatic storage duration - WG21月次提案文書を眺める(202年01月)
- [RFC][patch for gcc12][version 1] add -ftrivial-auto-var-init and variable attribute "uninitialized" to gcc
- P2795 進行状況
P2809R1 Trivial infinite loops are not Undefined Behavior
自明な無限ループを未定義動作ではなくする提案。
以前の記事を参照
このリビジョンでの変更は、提案する内容のC言語におけるセマンティクスとの一致についてSG1のフィードバックを追記したことです。
現在のCのセマンティクスでは次の2つのループは同じものとみなされません
// 少なくとも定数ではない変数 extern bool cond; void f1() { while (cond) { // ... } } void f2() { while (true) { if (!cond) break; // ... } }
この2つのループは意味的には等価なものに見えますが、f1()
の方のループの継続条件は定数式ではないためこの問題の下では異なります。Cのセマンティクスと一致するようにこの問題を解決すると、f1()
のループは未定義のままですがf2()
のループは未定義ではなくなります。
このことの問題点は、f2()
のループに対してコンパイラはその終了を仮定することができない(プログラマの意図的なものとして処理せざるを得ない)と言うことです。なぜそれが問題なのかというと、逆に意図的に並行処理の進行保証を活用して無限ループを書いている場合に、そのループが終了すると仮定した振る舞いや最適化(ループの削除やマージ、並べ替え)が行えなくなるためです。同様の問題は、f2
のようなループ内にreturn
やthrow
(スレッドの終了処理とみなされる)がある場合にも起こります。
実際に、一部のGPUではワープの1つが無限ループに陥った場合に同じスレッドブロック内のすべてのワープの進行を停止するものがあるようです。この振る舞いは、無限ループが未定義動作である場合は許容されますが、そうでない場合は規格違反となります。f2
のようなループによってそれが記述されている場合、現在は規格に適合した振る舞いですが、この提案の後(あるいはCのセマンティクスの下)では規格違反となります。
回避策として、f2
のようなループ中にstd::this_thread::yield()
を置いておくと言う方法が考えられます(他のスレッドに実行を委譲し続けることで上記動作を正当化する)。しかし、これは無限ループの中断という振る舞いを正当化するだけで、ループを最適化できないという問題は解決できません。
つまるところ、ループの継続条件が定数式であるかどうかという判定方法では不十分だということです。そのため、このリビジョンではCのセマンティクスとは一致しない解決を提案し、それをCにも適用することを提案しています。
提案ではまず、std::this_thread::halt()
というライブラリ関数を追加します。これは、その呼び出しがwhile(true) std::this_thread::yield();
の呼び出しと等価な関数であり、呼び出して使用するものというよりはある種のループをこれに置換する(後述)ためのものです。
次に、スレッドが最終的に行うと仮定して良い処理(これがあると無限ループは未定義ではなくなる処理)のリストに次の2つを追加します
std::this_thread::yield()
の呼び出し[[noreturn]]
関数の呼び出し
これはこの提案の主題とはあまり関係なく、議論の過程でこの2つがスレッドにおける進行保証を担うのに有効なもので見落とされていることが分かったために追加されています。
最後に、次に該当する無限ループをstd::this_thread::halt()
と等価なものであるとして、その呼び出しとして実装することを規定します(しても良いではなくしなければならない)。ここでは2つのオプションが提案されています
- オプション1 : 次の全てを満たすループ
- スレッドが最終的に行うと仮定して良い処理を何もしていない
- ループ継続条件が定数式で
true
となる - ループは次のいずれも含んでいない
- 直接の
break;
- ループの外側に抜ける
goto
return
文co_return
文co_await
式co_yield
式
- 直接の
- ループは次のどちらも含まない
- 例外を投げうる処理
std::longjmp()
の呼び出し
- オプション2 : 次の全てを満たすループ
- スレッドが最終的に行うと仮定して良い処理を何もしていない
- ループ継続条件が定数式で
true
となる - ループ本文が空
どちらのオプションでも、この条件を満たすループはスレッドが最終的に行うと仮定して良い処理をしていなくても未定義動作にはならず、プログラマの意図的なものとみなされます(つまり、最適化の対象となりません)。どちらにしても、以前(現在のC)のセマンティクスであるループ継続条件が定数式(でtrue
となる)という条件に制約をいくつか加えた形になっています。また、そのようなループをstd::this_thread::halt()
の呼び出しに置換してしまうことで無限ループは意図的なものであるとして終了しないが実行もしないという実装が可能になり、上記の一部のGPUが行なっているような動作(無限ループに突入すると実行中断)を正当化することができます。
P2810R1 is_debugger_present
is_replaceable
P2546で提案されているis_debugger_present()
をユーザーが置換可能にする提案。
以前の記事を参照
このリビジョンでの変更はP2546の最新リビジョン(R5)の内容を適用したことです。
P2811R5 Contract-Violation Handlers
↓
P2811R6 Contract-Violation Handlers
↓
P2811R7 Contract-Violation Handlers
契約プログラミングに、ユーザー提供の違反ハンドラを許可する提案。
以前の記事を参照
- P2811R1 Contract Violation Handlers - WG21月次提案文書を眺める(2023年04月)
- P2811R4 Contract Violation Handlers - WG21月次提案文書を眺める(2023年05月)
R5での変更は
R6での変更は
- ヘッダ名を
<contract>
から<contracts>
へ変更 contract_semantic
列挙型にobserve
列挙子を追加(P2877R0から)
このリビジョンでの変更は
- 文字列プロパティの期待されるエンコーディングについて注記
- 契約機能を使用するのに
<contracts>
のインクルードが必要ないことを明記 - 契約違反ハンドラの呼び出し元特定とテストに関するセクションを追加
- 適合実装が
location()
とcomment()
に何を提供できるかを明確化
などです。
P2814R1 Trivial Relocatability --- Comparing P1144 with P2786
オブジェクトの再配置(relocation)という操作に関する2つの提案を比較する文書。
以前の記事を参照
このリビジョンでの変更はよくわかりません。
この提案、特にtrivially relocationについてさらに時間をかけて議論していくことがEWGにおいて合意されています。relocation全体よりもまずtrivially relocationを優先し、なおかつ言語機能とする方向性が選好されているようです。
P2819R1 Add tuple protocol to complex
std::complex
にtuple
インターフェースを追加する提案。
以前の記事を参照
このリビジョンでの変更は
などです。
P2821R2 span.at()
↓
P2821R3 span.at()
std::span
に.at()
メンバ関数を追加する提案。
以前の記事を参照
R2での変更は、機能テストマクロの修正とP1024へのリンクを追加したことです。
このリビジョンでの変更は、typoの修正のみです。
P2828R2 Copy elision for direct-initialization with a conversion function (Core issue 2327)
型変換時のコピー省略のためのルールを明確化する提案。
以前の記事を参照
- P2828R0 Copy elision for direct-initialization with a conversion function (Core issue 2327) - WG21月次提案文書を眺める(2023年04月)
- P2828R1 Copy elision for direct-initialization with a conversion function (Core issue 2327) - WG21月次提案文書を眺める(2023年05月)
このリビジョンでの変更は、提案する文言の多岐にわたる修正のみです。
P2834R1 Semantic Stability Across Contract-Checking Build Modes
契約プログラミングにおいて、契約述語の存在がビルドモードによって異なる影響を他の言語機能に及ぼさないようにする提案。
以前の記事を参照
このリビジョンでの変更は、Appendixの各節はその問題に関する推奨事項を提案する形で終了するようにしたことです。
P2835R1 Expose std::atomic_ref
's object address
std::atomic_ref
が参照しているオブジェクトのアドレスを取得できるようにする提案。
以前の記事を参照
このリビジョンでの変更は、代替となるAPIの提案を追記したことです。
この提案のメインはstd::atomic_ref
にその参照先オブジェクトのアドレスを取得するための.data()
関数を追加することですが、これは別にアトミックなポインタではないのでここから取得したポインタ経由でアクセスすることができてしまい、これを間違って使用されるとデータ競合によるUBを起こしやすくなってしまいます。
そのため、この提案では代替設計として、.data()
がconst void*
を返すようにするか、uintptr_t
を返す別の関数を追加するものの2つを挙げています。
P2836R1 std::basic_const_iterator
should follow its underlying type's convertibility
std::basic_const_iterator<I>
をI
に対応するconst_iterator
に変換できるようにする提案。
以前の記事を参照
このリビジョンでは、以前の解決策が別の問題を引き起こすことなどから取り下げ、R0で問題としていたことは別のIssueとして分離し、問題の範囲を限定しています。
このリビジョンでは、ある定数イテレータ型I
に対してstd::basic_const_itertor<I>
が得られる場合に、それがI
に変換できないことを問題視しています。
void f(std::vector<int>::const_iterator i) {} auto v = std::vector<int>(); { auto i1 = std::ranges::cbegin(v); // returns vector<T>::const_iterator f(i1); // ok } auto t = v | stdv::take_while([](int const x) { return x < 100; }); { auto i2 = std::ranges::cbegin(t); // returns basic_const_iterator<vector<T>::iterator> f(i2); // error(現在のC++23では }
std::basic_const_itertor<std::vector<T>::iterator>
はstd::vector<T>::const_iterator
に変換可能ではない(.base()
を取れば可能ではある)ため、2つ目のf()
の呼び出しはエラーになります。ただ実は、これはC++20ではranges::cbegin()
がvector<T>::iterator
を返していたため通っていました(結果的には問題なかっただけで間違った振る舞いです)。
コンテナ型C
に対して、std::basic_const_itertor<C::iterator>
は意味的にはC::const_iterator
と同一のものであり、同等の扱いが可能であることが望ましく、それが期待と一致する振る舞いとなります。
このリビジョンでは、C++20の振る舞いを引継ぎつつも改善するために、std::basic_const_itertor<I>
がI
から変換可能な定数イテレータ型に同様に変換可能となるようにすることを提案しています。
namespace std { template<input_iterator Iterator> class basic_const_iterator { ... // 提案している変換演算子の宣言例 template<not-a-const-iterator CI> requires constant-iterator<CI> && convertible_to<Iterator const&, CI> constexpr operator CI() const &; template<not-a-const-iterator CI> requires constant-iterator<CI> && convertible_to<Iterator, CI> constexpr operator CI() &&; ... } }
変換先のイテレータ型CI
はbasic_const_iterator
ではなく(not-a-const-iterator
)、かつすでに定数イテレータである必要があります。これによって、const
性を破壊するような変換を行うことはできません。
なお、std::const_iterator_t
とranges::cbegin()
が一致しないことがある問題については、LWG Issue 3946にてstd::const_iterator_t
の定義を変更することで解決が図られます。
namespace std { template<range R> //using const_iterator_t = const_iterator<iterator_t<R>>; using const_iterator_t = decltype(ranges::cbegin(declval<R&>())); template<range R> //using const_sentinel_t = const_sentinel<sentinel_t<R>>; using const_sentinel_t = decltype(ranges::cend(declval<R&>())); }
おそらくこの変更はC++23に適用されます。この提案自体はC++26ターゲットですがC++23へバックポートすることを実装者への推奨事項とすることを提案しています。
なお、LEWGのレビューではこれをC++23のDRとすることに合意されています。
P2845R1 Formatting of std::filesystem::path
std::filesystem::path
をstd::format()
でフォーマット可能にする提案。
以前の記事を参照
このリビジョンでの変更は
fill-and-align
とwidth
について既存もフォーマット文字列へのリンクを追加range-format-spec
をpath-format-spec
で置き換え- 欠落していた文字コード変換についての規定を追加
などです。
P2865R1 Remove Deprecated Array Comparisons from C++26
C++20の一貫比較仕様に伴って非推奨とされた、配列間の比較を削除する提案。
以前の記事を参照
このリビジョンでの変更は
- オーディエンスにSG22を追加
- gccの警告表示について追記
- 提案する文言を最新のドラフトに適合させた
- オペランドと配列からポインタへのdecayを説明する文言の誤りを修正
- 文言レビューの結果を受けての修正
などです。
P2872R1 Remove wstring_convert
From C++26
C++17で非推奨とされたwstring_convert
をC++26で削除する提案。
以前の記事を参照
このリビジョンでの変更は
- コピペミスの修正
- 最初のレビュワーとしてSG16をアサイン
- 提案する文言を最新のドラフトに適合させた
- 標準ライブラリ実装がこれに関して警告を発し始めた時期の記録
- SG16レビューの概要と結果を追記
などです。
P2874R1 Mandating Annex D
↓
P2874R2 Mandating Annex D
Annex Dセクションにある機能の規定について、標準の他の部分と記法を合わせる提案。
以前の記事を参照
R1での変更は
このリビジョンでの変更は
- POD型/PODクラスの定義を斜体で表示する
is_pod
のMandatesをPreconditionsに切り替え- “shall (not) be” を “is (not)”へ修正
- 隣接するPreconditionsの統合
- 元のテキストから欠落していた
null
を追加 - D.24の番号変更
- 半開区間を示す際のマークアップが壊れていたのを修正
などです。
この提案はすでに、2023年6月の全体会議でC++26に適用されることが決まっています。
P2877R0 Contract Build Modes and Semantics
現在の契約プログラミングの仕様を、現在明らかなユースケースをサポートし将来の拡張に対して開いているようにする提案。
現在SG21で議論されている契約プログラミング仕様では、No_evalとEval_and_abortの2つのビルドモードのみが提供されており、翻訳単位間でのビルドモードの混合は実装定義のセマンティクスによって条件付きでサポートされています。
No_evalビルドモードでは全ての契約アノテーション(1つの関数に指定されている契約条件の全体)はignoreセマンティクスを持ち、契約アノテーションに含まれる各条件式は評価されず契約違反が起こることはありません。そのため、このビルドモードでは他のセマンティクスに影響を与えません。
Eval_and_abortビルドモードでは全ての契約アノテーションはenforceセマンティクスを持ち、契約アノテーションに含まれる各条件式が評価されそれがtrue
を返さない場合は契約違反処理プロセスが発生し、その終了後にプログラムは終了されます。
これによってC++の実装には制限が課されており、2つのビルドモードのプロパティはすなわちC++コントラクト機能のプロパティであるとみなされ、プラットフォーム固有のビルドモードまたは将来のビルドモードの追加によってこの2つのビルドモードのプロパティや前提に反するようなセマンティクスを持つビルドモードを追加することが妨げられます。これらのプロパティの重要な特徴は、翻訳単位内の全ての契約アノテーションが同じセマンティクスを持つことです。
この提案は、契約アノテーションのセマンティクスとビルドモードの制限を取り払い実装の自由度を最大化することを目指し、それによってC++契約プログラミング機能が満たすべき現在明らかなニーズを満足しつつ、将来のニーズに適応するための拡張の余地を残しておくようにしようとするものです。
提案で挙げられている現在明らかなニーズとは次のようなものです
- パッケージマネージャ
- 現在利用可能なパッケージマネージャの多くは、各パッケージについて単一のビルドのみを提供する。つまり、デバッグとリリースの両方を提供したりしない
- 契約アノテーションのセマンティクスがビルド時に決定しなければならない設計は、パッケージマネージャにとって大きな負担となる
- パッケージマネージャの管理者が契約機能のビルドモードのどちらを選択するにせよ、その決定はパッケージの作成者の契約機能の使用意欲にネガティブな影響を与える可能性がある
- パッケージマネージャの管理者がビルドモードの決定を各プロジェクトのビルドモードまで遅延する場合、エコシステム内での契約機能の使用は一貫性がなくなる可能性がある
- ことなるオブジェクトファイルが異なるビルドモードでビルドされている場合、条件付きサポートとなる
- パッケージとして配布されるソフトウェア
- 契約の解除
- 運用環境で契約チェックを利用する際の最も難しい問題の1つは、既存のコードに契約チェックを導入すること。
- 契約違反によるプログラムの終了のコストが高くつくため、契約違反によってプログラムが終了する場合、現在動作しているプログラムに契約チェックを導入する意欲が失われる
- 現在の2つのセマンティクス(ビルドモード)だけではこの問題を解消できない
- REPL
- REPL環境ではプログラムはコンパイルされないため、ビルドモードが存在しない
- 契約の評価が有効かどうか、及び違反時の振る舞いについては、ユーザーがいつでも変更できる動的なプロパティとなる
- デバッグ
これらのニーズは全て、現在のC++契約プログラミング仕様ではサポートできていません。これらのニーズを満たすには少なくとも次の要件を満たしている必要があります
- 標準に準拠した契約実装では、リビルドを必要とせずに契約アノテーションの評価の有効/無効を切り替えられなければらない
- 標準に準拠した契約実装では、ユーザーが契約アノテーションごとに、もしくは違反ハンドラの動作を通して、契約違反後に実行を継続するかを選択可能である必要がある
また、これらの要件がユーザーベースに適さない場合、実装はこれらの要件をサポートしないことも選択できるべきです。この提案の意図は、全てのC++実装や実行環境が全て同じ機能セットをサポートする必要があることではなく、それらの実装が全て標準に実装しwell-definedであり、他のC++プログラムに適用可能な動作に関するトレーニングと推論の対象になり続けるように維持することにあります。
その上でこの提案の変更は、大きく分けて次の2つです
- 契約アノテーションが評価される場合、その評価はignore、observe、enforceのいずれかのセマンティクスを持つ
- 契約アノテーションの個々の評価において、それがどのようなセマンティクスを持つかは実装定義とする
この提案は、(ビルドモードの)セマンティクスとしてobserveを1つ追加するとともに、契約アノテーションの評価のセマンティクスを契約アノテーション全体からその個々のプロパティとすることで、契約アノテーションを持つもののコンパイル時プロパティが契約アノテーションのセマンティクスに依存しない(できない)ようにするものです。
これによって、P2834R0で提案されている契約アノテーションに関する三原則の1つ目(契約アノテーションがビルドモードによって他の言語機能に影響を与えないこと)が満たされ、翻訳単位間で契約アノテーションのセマンティクスが混合していることも許可されます。
この提案の後でも、実装は現在の2つのビルドモードだけをサポートすることを選択することができ、また、コンパイラはリンク時やコンパイル時、実行時で契約アノテーションのセマンティクスを選択できるようなビルドオプションを提供することもできます。
この提案の内容はどうやらSG21の合意のもとで契約仕様に導入(Contratcts MVPにマージ)されたようで、P2811R6の採用と相まって、C++契約プログラミング仕様はビルドモードの概念から解き放たれることになります。そこでは、この提案で解説されているように、契約条件のセマンティクス(評価されるかどうか、評価された時にどうなるかなど)はignore・observe・enforceのいずれかが契約アノテーションごとに決定され、その決定は実装定義となります。さらに、observeの場合に契約違反を起こした場合の振る舞い(即終了するのか、例外を投げるのか、継続するのか)は違反ハンドラの差し替えによってユーザーがカスタマイズすることができます。
- P2834R0 Semantic Stability Across Contract-Checking Build Modes - WG21月次提案文書を眺める(2023年05月)
- P2811R4 Contract Violation Handlers - WG21月次提案文書を眺める(2023年05月)
- 2023-06 Varna ISO C++ Committee Trip Report — First Official C++26 meeting! : r/cpp - reddit
- P2877 進行状況
P2878R2 Reference checking
↓
P2878R3 Reference checking
↓
P2878R4 Reference checking
プログラマが明示的に関数の戻り値に関するライフタイム注釈を行えるようにする提案。
以前の記事を参照
R2での変更はサンプルコードの修正などです。
R3での変更は
do
式/パターンマッチングの未解決の問題に対する解決策の追加- 一時オブジェクトの名前付き変数への代入がエラーになることを追加
- 関数が個別に分析されることを示すために、再帰関数呼び出しの例を追加
- ラムダ式の別の例を追加
- Usageセクションの追加
- Viral Attribution Effortセクションの追加
このリビジョンでの変更は
- サンプルコードの修正
- FAQの追加(ポインタを対象としないことについて)
などです。
この提案はSG23のレビューによってこれ以上議論されないことが決定されています。
P2885R0 Requirements for a Contracts syntax
C++契約プログラミング機能の構文の選択のための評価基準や要件をまとめる文書。
少し上のP2877R0などの採択によって、契約プログラミング機能の意味論の部分はほぼ固まりつつあります。残っている大きな問題は構文の選択に関するものです。
現在のContratcts MVPでは属性likeな構文を採用しており、これはC++20の契約プログラミング機能から受け継いでいるものです。それに対して、クロージャベースの構文(P2461R1)や条件中心構文(P2737R0)が提案され、また他の構文を考えることもできます。しかし、現在のところこれらの構文提案を統一的に比較し評価するためのフレームワークが存在しません。
この提案は、契約構文の選択のために、契約構文に求められる要件をまとめその比較基準を提供しようとするものです。ただし、この提案は構文を選択することを意図しておらず、既存提案の詳細な分析を行おうとするものではなく、あくまで契約機能の構文に求められる要件やその比較基準を確立することを目的としたものです。
提案されている要件の概要は次のようなものです
- 基本要件
- 互換性の要件
- 機能性の要件
- 将来の進化に備えた要件
- 非参照非
const
引数の参照- 事後条件において、非参照非
const
引数を参照する拡張が可能である
- 事後条件において、非参照非
- 明示的なキャプチャ
- 契約アノテーションで使用する変数を明示的にキャプチャする拡張が可能である
- 戻り値の構造化束縛
- 事後条件において、戻り値を構造化束縛して参照する拡張が可能である
- 契約アノテーションの再利用
- 同じ事前/事後条件の集合を共有する関数の間で、それを抽出してまとめて再利用できるようにする拡張が可能である
- メタアノテーション
- 引数をとるメタアノテーション
- メタアノテーション構文はさらに、引数をとることができるように拡張可能である
- ユーザー定義メタアノテーション
- 標準で定義されるものと競合しない、ユーザー定義のメタアノテーションを可能にする拡張が可能である
- 無視できないメタアノテーション
- 標準属性の無視可能性ルールに適合しないようなメタアノテーションを可能にする拡張が可能である
- 一次情報と二次情報の識別
- クラス不変条件
- クラス不変条件を表現するための新しい構文を導入可能であること
- 手続き型インターフェース
- P0465R0で提案されている手続き型インターフェースに対応可能であること
requires
節- 契約アノテーションを
requires
節で制約できるようにする拡張が可能であること
- 契約アノテーションを
- より一般的な拡張性
- ここの要件のリストは大量だが、これでも将来的に契約機能に必要になる可能性のある拡張を網羅していない
- 非参照非
これらの要件は排他的なものもあるため必ずしも全てを満たす必要はなく、それはトレードオフとして比較することを意図しています。
- P2461R0 Closure-based Syntax for Contracts - [C++]WG21月次提案文書を眺める(2021年10月)
- P2737R0 Proposal of Condition-centric Contracts Syntax - WG21月次提案文書を眺める(2023年01月)
- P2487R0 Attribute-like syntax for contract annotations - [C++]WG21月次提案文書を眺める(2021年11月)
- P2885 進行状況
P2898R1 Build System Requirements for Importable Headers
モジュールにおけるインポート可能なヘッダ(importable header)というものを修正する提案。
以前の記事を参照
このリビジョンでの変更は
- 誤解に基づいていたため、インポート構文に関するセクションを削除
- インポート可能なヘッダの導入におけるインクリメンタルビルドのパフォーマンスの重要性を強調
- フィードバック等に基づいて、推奨事項のセクションを書き換え
- 新しい結論を反映するためにタイトルを変更
などです。
このリビジョンでの提案(推奨事項)は
- 依存関係のスキャンでは(ヘッダユニットの)インポートをエミュレートする必要がある
- インポート可能なヘッダの検出を許可する
- ビルドグラフで動的ノードをサポートしないビルドシステムにおいても、インポート可能なヘッダが宣言されたリストが必要
などです。
P2902R0 constexpr 'Parallel' Algorithms
並列アルゴリズムを定数式で使用できるようにする提案。
並列アルゴリズムとはC++17で追加された実行ポリシーを受け取るオーバーロードの事で、これれらのものは並列化やベクトル化によってアルゴリズムの実行時パフォーマンスを向上させることが目的であるため、cosntexpr
指定はされておらず定数式では使用できません。
並列アルゴリズムも含めたアルゴリズムは他の処理や他のアルゴリズムと組み合わせて使用するものであり、他のアルゴリズムや標準ライブラリの多くのものがconstexpr
対応を果たしている中で並列アルゴリズムがconstexpr
対応しない場合、そのような並列アルゴリズムと組み合わされた一連の処理を定数式で実行することができなくなります。
その場合でも、std::is_constant_evaluated()
やif consteval
を使用して分岐をすればとりあえず対応はさせられますが、そもそも並列アルゴリズムがconstexp
であればより単純に目的を達成できます。
この提案はそのような目的のために並列アルゴリズムをconstexpr
対応させることを目指すものです。ただし、コンパイル時にも並列化を要求するものではありません。
この提案では、全ての実行ポリシーを定数式で指定可能とすることを提案しており、その実装はコンパイル時に対応する通常のアルゴリズム関数に処理を委譲することでコンパイル時実行することを意図しています。
P2904R0 Removing exception in precedence rule(s) when using member pointer syntax
メンバポインタ構文で()
の使用を許可する提案。
クラスC
のメンバBar
のメンバポインタを取得する構文は&C::bar
のように書きますが、この時&(C::bar)
のように括弧で括る形式は明確に禁止されており、コンパイルエラーとなります。
なぜこのような制限があるのかは不明ですが、この制限は不要のものと思われ他の場合と一貫していないため、この提案はこの制限を取り払うことを提案するものです。
struct C { void Bar(int); }; int main() { void (C::*ptr)(int) = &(C::Bar); // MSVCはok、clang/gccはng }
また、これは実装間で挙動の相違を生み出してしまっているようで、MSVCでは意図通り(この提案の提案通りに)になるようです。
P2905R0 Runtime format strings
↓
P2905R1 Runtime format strings
std::make_format_args()
が左辺値でフォーマット対象の値を受け取るようにする提案。
std::format
はフォーマット文字列のコンパイル時チェックを行うため、フォーマット文字列はコンパイル時に決定していなければなりません。そのため、フォーマット文字列を実行時に与えたい場合はstd::vformat()
を使用します。
std::string str = translate("The answer is {}."); // gettextライブラリによる翻訳後文字列をフォーマット文字列として使う std::string msg = std::vformat(str, std::make_format_args(42));
ただ、このv
系の型消去APIはテンプレートの肥大化を回避するためのAPIであり、エンドユーザーではなくフォーマット内部実装や独自のフォーマット関数を作成する場合などに使用するものであるため、実行時のフォーマット文字列指定のために使用するのは本来の用途ではありません。
特に、std::make_format_args()
は簡単に間違った使い方ができてしまいます。
std::string str = "{}"; std::filesystem::path path = "path/etic/experience"; auto args = std::make_format_args(path.string()); // path::string()はprvalueを返す std::string msg = std::vformat(str, args); // UB、argsの参照するstd::stringオブジェクトは寿命が尽きている
std::make_format_args()
はフォーマット対象の値を型消去して渡すためのものであり、内部でその値を保持するわけではなく、一時オブジェクトを渡してもその寿命は延長されません。このことは、関数のインターフェースや効果などから読み取ることは難しく、これはあくまで内部実装のための関数です。
この提案はstd::make_format_args()
のこの問題を改善するために、引数型を非const
左辺値参照をとるように変更することで一時オブジェクトを渡すとコンパイルエラーになるようにしようとするものです。
namespace std { // make_format_args()の現在の宣言例 template<class Context = format_context, class... Args> format-arg-store<Context, Args...> make_format_args(Args&&... fmt_args); // この提案による変更 template<class Context = format_context, class... Args> format-arg-store<Context, Args...> make_format_args(Args&... fmt_args); }
これによって、先ほどのようなコードはコンパイルエラーとして弾かれるようになります。
これは破壊的変更となりますが、この提案はC++23へのDRとすることがLEWGによって承認されています。
std::make_format_args()
の利用率は低いと思われ、コンパイルエラーとなるのは一時オブジェクトか右辺値を渡した場合なので、正しい使用法のほとんどはこの提案の後でも変更の必要はありません。エラーとなる場合はstd::forward()
してしまっているか、一時オブジェクトを直接渡しているかの場合のどちらかのはずで、前者はstd::forward()
を削除する修正が必要となり、後者は一時オブジェクトの寿命を延長する変更が必要となります。
この提案のR0では、実行時のフォーマット文字列指定のためのAPI(std::runtime_format
)も同時に提案していましたが、それは別の提案に分離されました。
- P2418R2 Add support for
std::generator
-like types tostd::format
- [C++]WG21月次提案文書を眺める(2021年10月) - P2905 進行状況
P2906R0 Structured bindings for std::extents
std::extents
に構造化束縛サポートを追加する提案。
std::extents
はstd::mdspan
に対してその要素数を指定するためのクラステンプレートです。
// double型の3x3行列を表すmdspan using mat33d = std::mdspan<double, std::extents<std::size_t, 3, 3>>; // double型の3xN行列を表すmdspan(Nは実行時に決定) using mat3nd = std::mdspan<double, std::extents<std::size_t, 3, std::dynamic_extante>>; // double型の2次元行列を表すmdspan(サイズは実行時に決定) using matd = std::mdspan<double, std::dextents<std::size_t, 2>>;
std::mdspan
のメンバ関数.extents()
からこのstd::extents
オブジェクトは取得できて、std::extents
の.extent(n)
からn + 1
次元の要素数を取得できます。特に実行時にその要素数が決まる場合はこれを用いてループを回す必要があります。
// 3次元mdspanを出力する関数 template<typaname D, typename E, typename L, typename A> void print_3d_mdspan(std::mdspan<D, E, L, A> mat) { using I = std::mdspan<D, E, L, A>::index_type; // 要素数情報の取得 const auto e = mat.extents(); // 全要素の走査 for (I z = 0; z < e.extent(2); ++z) { for (I y = 0; y < e.extent(1); ++y) { for (I x = 0; x < e.extent(0); ++x) { // 要素取得 const auto v = mat[z, y, x]; // 出力 std::print("[{}, {}, {}] = {}\n", z, y, x, v); } } } }
現在のstd::extents
のAPIは限定されており、次元ごとの要素数の取得はこうするしかありません。
この提案は、std::extents
を構造化束縛対応させることで、各次元の要素数取得をより簡易に行えるようにしようとするものです。
先ほどの例は例えば次のように書き直せます
// 3次元mdspanを出力する関数 template<typaname D, typename E, typename L, typename A> void print_3d_mdspan(std::mdspan<D, E, L, A> mat) { using I = std::mdspan<D, E, L, A>::index_type; // 要素数情報の取得 const auto [depth, height, width] = mat.extents(); // 👈 構造化束縛によって直接要素数を取得する // 全要素の走査 for (I z = 0; z < depth; ++z) { for (I y = 0; y < height; ++y) { for (I x = 0; x < width; ++x) { // 要素取得 const auto v = mat[z, y, x]; // 出力 std::print("[{}, {}, {}] = {}\n", z, y, x, v); } } } }
さらに、P1061にて提案されている構造化束縛でパラメータパックを導入する機能を加味すると、このコードはさらに簡易に書くことができます
// 3次元mdspanを出力する関数 template<typaname D, typename E, typename L, typename A> void print_3d_mdspan(std::mdspan<D, E, L, A> mat) { using I = std::mdspan<D, E, L, A>::index_type; // 要素数情報の取得 const auto [...es] = mat.extents(); // 全要素の走査 for (const auto [...is] : std::cartesian_product(std::views::iota(0, es)...)) { // 要素取得 const auto v = mat[is...]; // 出力 std::print("[{}, {}, {}] = {}\n", is..., v); } }
このことを実装するにあたって、静的エクステントをどう扱うかについて選択肢があります。静的エクステントはコンパイル時に定まっている定数値であるため、構造化束縛でそのことを維持するかどうかで少し実装が異なります(構造化束縛はconstexpr
指定できない)。
- 静的エクステントを実行時の値に降格する
std::integral_constant
などを利用して、コンパイル時定数であることを維持する
オプション1に比べてオプション2では実装の複雑さが増大し、定数値の扱いが難しくなります(std::integral_constant
の暗黙変換によって容易に定数性が失われるため)。
この選択に関してはLEWGの投票において決定される予定で、(この記事を書いてる時点では)まだその投票は行われていません。
- std::mdspan - yohhoyの日記
- P1061R2 Structured Bindings can introduce a Pack - WG21月次提案文書を眺める(2022年04月)
- P2906 進行状況
P2910R0 C++ Standard Library Ready Issues to be moved in Varna, Jun. 2023
6月に行われたVarna会議でWDに適用されたライブラリに対するIssue報告の一覧
- 2994. Needless UB for basic_string and basic_string_view
- 3884. flat_foo is missing allocator-extended copy/move constructors
- 3885. 'op' should be in [zombie.names]
- 3887. Version macro for allocate_at_least
- 3893. LWG 3661 broke atomic<shared_ptr
> a; a = nullptr; - 3894. generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>) should not be noexcept
- 3903. span destructor is redundantly noexcept
- 3904. lazy_split_view::outer-iterator's const-converting constructor isn't setting trailing_empty_
- 3905. Type of std::fexcept_t
- 3912. enumerate_view::iterator::operator- should be noexcept
- 3914. Inconsistent template-head of ranges::enumerate_view
- 3915. Redundant paragraph about expression variations
- 3925. Concept formattable's definition is incorrect
- 3927. Unclear preconditions for operator[] for sequence containers
- 3935. template
constexpr complex& operator=(const complex &) has no specification - 3938. Cannot use std::expected monadic ops with move-only error_type
- 3940. std::expected<void, E>::value() also needs E to be copy constructible
P2911R0 Python Bindings with Value-Based Reflection
提案中の値ベースリフレクションによってPythonバインディングの作成を簡素化できることを示す文書。
この文書は、pybind11によるPythonバインディングの作成に値ベースリフレクション(P1240R2)を活用することでその作業を簡素化できることを示し、その利点と課題についての議論を行おうとするものです。
例えば次のような列挙型があるとき
struct Execution { enum class Type { new_, fill, partial, cancelled, rejected }; };
これのPythonバインディングを作成するコードは次のようになります
py::enum_<Execution::Type>(/*binding scope*/, "Type") .value("new_" , Execution::Type::new_) .value("fill" , Execution::Type::fill) .value("partial" , Execution::Type::partial) .value("cancelled", Execution::Type::cancelled) .value("rejected" , Execution::Type::rejected);
これは列挙型のメンバを手動で展開しているため、繰り返し同じようなコードを書かなければならず、Execution::Type
が更新されるとこちらも手動で更新しなければならないなど、不便な面があります。
対して、値ベースリフレクションを利用すると同等のバインディングは次のようなコードで完結させられます。
bind_enum<Execution::Type>(/*binding scope*/);
bind_enum()
はこの目的に書かれた汎用的なコードであり、ライブラリなどとして提供されることを意図しています。そして、このbind_enum()
は次のように実装できます
// 型名だけを取り出す関数 template<typename T> std::string basename() { auto name = std::string{name_of(^T)}; // ^T reflects type T if (size_t pos = name.rfind(':'); pos != std::string::npos) { return name.substr(pos + 1); } return name; } template<typename EnumT, typename Scope> void bind_enum(Scope& s) { auto enum_ = py::enum_<EnumT>(s, basename<EnumT>().c_str()); // 列挙型EnumTの全てのメンバに対してイテレートする template for (constexpr auto e : members_of(^EnumT)) { enum_.value(name_of(e), [:e:]); // [:e:] un-reflects e } }
このように、値ベースリフレクションを用いると、型のメンバなどを列挙しその名前を取得してそれを特定の関数に投げることを繰り返すような処理をかなり自動化して書くことができ、現在手動で書かざるをえずに不便やバグの元となっている部分をより簡易かつ安全に記述することができます。
提案には、そのほかにもクラスメンバや継承、関数オーバーロードなどの同様の例が記載されています。
これらの試みによって得られた利点と課題は次のようなものです
- 利点
- 課題
この文書はこのようなことを行うライブラリを提案しているのではなく、値ベースリフレクションの利点を示すとともに足りないものやあった方がいい機能などを示し、その議論を前進させようとするものです。
- P1240R2 Scalable Reflection - WG21月次提案文書を眺める(2022年01月)
- P2320R0 The Syntax of Static Reflection - WG21月次提案文書を眺める(2021年02月)
P2912R0 Concurrent queues and sender/receivers
P1958R0で提案されているbuffer_queue
にP2300のsender/receiver
モデルをベースとしたAPIを拡張する提案。
P1958R0のbuffer_queue
は、内部の固定長バッファをリングバッファとして使用して同期/非同期の両方のAPIを提供することで並行プログラミングで使用可能な並行キューを提供しようとするものです。
この提案は、このbuffer_queue
をP2300のsender/receiver
に適応可能なように拡張しようとするものです。また、それに伴ってAPIのスタイルも変更されています。
提案及び実装より、buffer_queue
の概要
template <typename T, typename Alloc = std::allocator<T>> class buffer_queue { public: using value_type = T; explicit buffer_queue(size_t max_elems, Alloc alloc = Alloc()); ~buffer_queue() noexcept; // observers bool is_closed() noexcept; size_t capacity() const noexcept; // modifiers void close() noexcept; // 同期pop T pop(); std::optional<T> pop(std::error_code& ec); std::optional<T> try_pop(std::error_code& ec); // 同期push(コピー) void push(const T& x); bool push(const T& x, error_code& ec); // used to be wait_push bool try_push(const T& x, error_code& ec); // 同期push(ムーブ) void push(T&& x); bool push(T&& x, error_code& ec); // used to be wait_push bool try_push(T&& x, error_code& ec); // 非同期push/pop sender auto async_push(const T& x) noexcept(is_nothrow_copy_constructible_v<T>); sender auto async_push(T&& x) noexcept(is_nothrow_move_constructible_v<T>); sender auto async_pop() noexcept; };
この提案で追加されたのは、async_~
という名前の3つの操作です。これらは非同期的にpush/pop
を行う関数で戻り値としてsender
を返します。これによって、非同期操作の待機や継続はP2300のsender
アルゴリズムを利用して行うことができます。
また、同期操作のインターフェースも変更されており、操作の成否を判定できるインターフェースがstd::error_code
を受け取るようになり、pop()
操作では戻り値がstd::optional
を返すようにされています。
P2915R0 Proposed resolution to CWG1223
後置戻り値型関数宣言との曖昧さを解消するために、auto
の存在を構文的に扱うようにする提案。
後置戻り値型関数宣言はauto
を戻り値型に持つ関数宣言でのみ考慮されますが、この制限は意味的なものであり構文的なものではありませんでした。それによって、C++11以前は有効だった変数宣言や式が関数宣言として扱われてしまう場合がありました。
struct A { A(int *); A *operator()(void); int B; }; int *p; typedef struct BB { int C[2]; } *B, C; void foo() { A (p)()->B; // ng、関数宣言として扱われる(C++11以降 A a(B ()->C); // ng、関数宣言 or 変数宣言 sizeof(B ()->C[1]); // ng、関数宣言に対するsizeof or 式に対するsizeof }
この提案は、この場合にauto
の存在を構文的にもチェックし制限することでこの問題を解決しようとするものです。この提案後、上記コードは次のようにコンパイルされるようになります
void foo() { A (p)()->B; // ok、式(A::Bへのメンバアクセス) A a(B ()->C); // ok、変数宣言 sizeof(B ()->C[1]); // ok、式に対するsizeof }
提案文書より、その他の例
struct M; struct S { S* operator()(); int N; int M; void mem(S s) { auto(s)()->M; // S::Mは::Mを非表示にし、これは式とみなされる } }; void f(S s) { { auto(s)()->N; // 式 auto(s)()->M; // 関数宣言(::Mが見えているため) } { S(s)()->N; // 式(autoがないため関数宣言とはみなされない) S(s)()->M; // 式(autoがないため関数宣言とはみなされない) } }
この問題はIssue1223としてC++11に対して2010年に提出されて以降放置されていましたが、C++23にてauto(x)
のdecay-copy構文が導入されたことによって影響が大きくなることがわかったため、この提案で迅速に解決されました(既に2023年6月の会議で承認され、C++26WDに取り込まれています)。
P2917R0 An in-line defaulted destructor should keep the copy- and move-operations
↓
P2917R1 An in-line defaulted destructor should keep the copy- and move-operations
クラス定義内でdefault
宣言されたデストラクタがある場合に、コピー/ムーブコンストラクタ及び代入演算子を暗黙default
宣言するようにする提案。
C++11以降、default
含むユーザー宣言デストラクタが存在する場合、そのクラスのコピーコンストラクタは暗黙的にdefault
定義されますがそれは非推奨とされます。また、そのようなクラス型のムーブコンストラクタ及び代入演算子は暗黙定義されません。
class Apple { public: ~Apple() = default; // コピーコンストラクタ及び代入演算子は暗黙default定義される(非推奨の振る舞い) // ムーブコンストラクタ及び代入演算子は暗黙定義されていない };
このApple
のようなクラスをコピー/ムーブ可能にしたければ、全てのメンバ関数を明示的に宣言しなければなりません。
class Apple { public: ~Apple() = default; // コピーを有効化 Apple(const Apple&) = default; Apple& operator=(const Apple&) = default; // ムーブを有効化 Apple(Apple&&) = default; Apple& operator=(Apple&&) = default; };
このことは、特殊メンバ関数のデフォルト宣言というボイラープレートコードを量産しており、また、デストラクタは他の特殊メンバ関数とは独立しているべきだとして、この提案はこの制限を解除しようとするものです。
この提案では、クラスの最初のデストラクタの宣言がdefault
宣言である時、コピー/ムーブコンストラクタ及び代入演算子を暗黙default
宣言するようにします。それによって、上記のApple
のようなクラスは追加の特殊メンバ関数の宣言なしでコピーとムーブが可能になります。
class Apple { public: ~Apple() = default; // コピー/ムーブコンストラクタ及び代入演算子は暗黙default定義される };
ただし、最初のデストラクタの宣言がdefault
ではない場合、すなわちクラス定義外でdefault
宣言されている場合は現行通りにムーブコンストラクタ/代入演算子は定義されず、コピーコンストラクタ/代入演算子の暗黙default
定義は非推奨です。
class Apple { public: ~Apple(); // コピーコンストラクタ及び代入演算子は暗黙default定義される(非推奨の振る舞い) // ムーブコンストラクタ及び代入演算子は暗黙定義されていない }; Apple::~Apple() = default;
この提案の内容は、以前のコードに破壊的変更をもたらします。
// 現在このクラスはムーブ可能ではないが、この提案の後ではムーブ可能となる struct A { std::unique_ptr<int> pi; virtual ~A() = default; }; // 現在このクラスはムーブ可能ではないが、この提案の後ではムーブ可能となる struct B { std::string s; virtual ~B() = default; }; void f() { B b1; B b2; b2 = std::move(b1); // 現在はコピーされているが、この提案の後ではムーブされる } // 現在このクラスはムーブ可能ではないが、この提案の後ではムーブ可能となる struct C { // ムーブ操作を無効化することを意図するデストラクタ宣言 ~C() = default; }; struct Base { int x{}; virtual ~Base() = default; // その他virtualメンバ関数 }; struct Derived : Base { int y{}; }; void g() { Base b{}; Derived a = b; // スライスが発生する、この提案後は気付きづらくなる可能性がある }
- C++0xにすごい変更が来た - 本の虫
- 特殊メンバ関数とコンパイラによる暗黙宣言 - yohhoyの日記
- コンストラクタが暗黙に宣言されるとき、されないとき - Qiita
- Tutorial: When to Write Which Special Member - foonathan::blog()
- P2917 進行状況
P2918R0 Runtime format strings II
↓
P2918R1 Runtime format strings II
std::format()
の実行時フォーマット文字列のためのAPIを追加する提案。
モチベーションの一部は少し上のP2905R1と共通しているのでそちらもご覧ください。
std::format()
のフォーマット文字列はコンパイル時検査される関係上、コンパイル時に確定している文字列でなければなりません。実行時文字列によってフォーマット文字列を指定したい場合はstd::vformat
などを使用することになるのですが、これは内部実装用のもので利用しやすいAPIではありませんでした。
この提案は、実行時文字列によるstd::format
のための専用のAPIを追加することで、実行時フォーマット文字列によるstd::format
の利便性を改善しようとするものです。
提案では、std::runtime_format()
という関数に実行時フォーマット文字列を渡し、この関数の戻り値をstd::format()
のフォーマット文字列(第一引数)として渡すことで実行時フォーマット文字列によるフォーマットをstd::format()
に組み込みます。
void f(std::string_view str) { // 現在の実行時文字列によるフォーマット文字列指定 auto rfmt1 = std::vformat(str, std::make_format_args(42)); // この提案 auto rfmt2 = std::format(std::runtime_format(str), 42); }
std::runtime_format()
は受け取った文字列をラップする専用の型を返し、std::basic_format_string
がその型を受け取れるようにすることでこのサポートを行えるようにしており、フォーマット文字列の検証はコンパイル時と同様にstd::basic_format_string
のコンストラクタで(実行時に)行われます。
P2920R0 Library Evolution Leadership's Understanding of the Noexcept Policy History
LEWGの管理者が認識する、noexcept
の指定ルールについてのこれまでとこれからの議論についてのまとめ。
おそらく、5月公開の提案においてLakos Ruleに関する提言が相次いだことを受けてのものです。Lakos Rule周りの議論についての歴史がまとめられており、今回提出された提案を受けてどう議論をしていくのかなどがスライドで説明されています。
P2921R0 Exploring std::expected
based API alternatives for buffer_queue
P2912R0で提案されているbuffer_queue
のAPI拡張について、std::expected
を返すAPIを検討する提案。
少し上のP2912R0によるP1958R0のbuffer_queue
のAPI拡張においては、一部の失敗しうる操作に対してstd::error_code
を受けてそこにエラー情報を出力する無例外APIを提案していました。
template <typename T, typename Alloc = std::allocator<T>> class buffer_queue { ... // 同期pop std::optional<T> pop(std::error_code& ec); std::optional<T> try_pop(std::error_code& ec); // 同期push(コピー) bool push(const T& x, error_code& ec); bool try_push(const T& x, error_code& ec); // 同期push(ムーブ) bool push(T&& x, error_code& ec); bool try_push(T&& x, error_code& ec); ... };
この提案は、これらのAPIをstd::error_code
を受け取るのではなくstd::expected
を返すようにすることを検討するものです。
この提案ではいくつかのタイプのAPIを検討しています。
まず一つは、std::error_code
の代わりにstd::nothrow
を渡してstd::expected
を受けることを明示するタイプです。
template <typename T, typename Alloc = std::allocator<T>> class buffer_queue { ... // P2912R0のAPIの同期push void push(const T& x); bool push(const T& x, error_code& ec); // expectedを返すAPI void push(const T&); auto push(const T&, nothrow_t) -> expected<void, conqueue_errc>; ... };
例
int main() { buffer_queue<T> q{}; // P2912R0 std::error_code ec; if (q.push(5, ec)) return; println("got {}", ec); // この提案 if (auto result = q.push(5, nothrow)) return; else println("got {}", result.error()); }
この場合の利点は、API呼び出し前にerror_code
オブジェクトを用意しなくても良いところです。
もう一つは、例外を投げうるAPIを削除して、全てstd::expected
を返すAPIに統一するものです。
template <typename T, typename Alloc = std::allocator<T>> class buffer_queue { ... // P2912R0のAPIの同期push void push(const T& x); bool push(const T& x, error_code& ec); // expectedを返すAPI auto push(const T&) -> expected<void, conqueue_errc>; ... };
int main() { buffer_queue<T> q{}; // 無例外の例 { // P2912R0 std::error_code ec; if (q.push(5, ec)) return; println("got {}", ec); // この提案 if (auto result = q.push(5)) return; else println("got {}", result.error()); } // 例外を投げる例 { // P2912R0 try { q.push(5); } catch(const conqueue_error& e) { ... } // この提案 try { q.push(5).or_else([](auto code) { throw conqueue_error(code); }); } catch(const conqueue_error& e) { ... } } }
この場合、APIは基本的に例外を投げないため、標準ライブラリの他の部分(特にコンテナ)と一貫性がなく、1つ目のAPI候補よりも劣っています。
また、この提案ではtry
系関数について連想コンテナのtry_emplace()
の設計を踏襲して、キューに値が挿入されない場合は渡された引数は変更されないようにすることを推奨しています。そしてその場合に、それに反してstd::expected
のエラー値で渡された値を返すAPIについても検討しており、その場合はユーザーに対する負担が大きくなると報告されています。
もう一種類、try_emplace()
の設計を踏襲する方針のもと、1つ目のAPI候補においてtry
系関数でstd::nothrow
の指定を省略するAPIについても検討されています。
とはいえこの提案の結論としては、std::expected
ベースのこれらのAPIがP2912R0で提案されているAPIと比較して明確に改善されているとは思えない、と報告しています。
P2922R0 Core Language Working Group "ready" Issues for the June, 2023 meeting
6月に行われたVarna会議でWDに適用されたコア言語に対するIssue報告の一覧。
- 170. Pointer-to-member conversions
- 1353. Array and variant members and deleted special member functions
- 1642. Missing requirements for prvalue operands
- 1973. Which parameter-declaration-clause in a lambda-expression?
- 2485. Bit-fields in integral promotions
- 2519. Object representation of a bit-field
- 2542. Is a closure type a structural type?
- 2550. Type "reference to cv void" outside of a declarator
- 2552. Constant evaluation of non-defining variable declarations
- 2663. Example for member redeclarations with using-declarations
- 2683. Default arguments for member functions of templated nested classes
- 2697. Deduction guides using abbreviated function syntax
- 2698. Using extended integer types with z suffix
- 2699. Inconsistency of throw-expression specification
- 2708. Parenthesized initialization of arrays
- 2710. Loops in constant expressions
- 2711. Source for copy-initializing the exception object
- 2712. Simplify restrictions on built-in assignment operator candidates
- 2713. Initialization of reference-to-aggregate from designated initializer list
- 2715. "calling function" for parameter initialization may not exist
- 2716. Rule about self-or-base conversion is normatively redundant
- 2717. Pack expansion for alignment-specifier
- 2718. Type completeness for derived-to-base conversions
- 2719. Creating objects in misaligned storage
- 2720. Template validity rules for templated entities and alias templates
- 2721. When exactly is storage reused?
- 2722. Temporary materialization conversion for noexcept operator
- 2723. Range of representable values for floating-point types
- 2724. Clarify rounding for arithmetic right shift
- 2729. Meaning of new-type-id
- 2732. Can importable headers react to preprocessor state from point of import?
- 2750. construct_at without constructor call
P2925R0 inplace_vector
- D0843R7 LEWG presentation
提案中のinplace_vector
の紹介スライド。
inplace_vector
はP0843R8で提案されています。
ここでは、要素を追加するインターフェースについて解説されているほか、各インターフェースとstd::vector
との比較ベンチマークの結果も記載されています。
P2926R0 std::simd types should be regular - P2892R0 LEWG presentation
P2892R0の解説スライド。
P2892R0の反対意見がいくつか紹介された上で、それに対する回答が肯定的立場から説明されています。
P2929R0 simd_invoke
std::simd
で組み込み関数の使用を簡易にする呼び出しラッパ関数の提案。
C++26を目指して提案中のstd::simd
は、任意の環境で効率的なデータ並列処理を簡易に書くことができるようにするためのデータ並列型です。そのため、std::simd
は多くのプラットフォームで使用可能なSIMD演算等を意識して設計されています。
それによって、特定のハードウェアでは組み込み関数(intrinsic)を使用してより効率的なSIMD演算を呼び出したい場合が想定されます。そのためには、std::simd
が保持する値を環境のSIMDレジスタに移して、それを用いて組み込み関数を実行し、その結果を再びstd::simd
に保持させる、といったことをする必要があります。
このために、現在のstd::simd
にはネイティブのSIMDデータ型とやり取りするためのコンストラクタと変換演算子を実装定義で追加することが許可されています
template<class T, class Abi = ...> class simd { ... // ネイティブのSIMDデータ型からの変換コンストラクタ constexpr explicit simd(const implementation-defined& init); // ネイティブのSIMDデータ型への変換演算子 constexpr explicit operator implementation-defined() const; ... }
これを用いて、std::simd
を組み込み関数呼び出しのために必要なネイティブのSIMDデータ型へ変換しで組み込み関数を呼び出すことができます。また、std::simd
のコンストラクタはこの逆の変換を行えるため、その結果をstd::simd
型に戻すこともそのまま行えます。
// _mm256_addsub_psは奇数要素を足し算し、偶数要素を引き算するintel AVXの組み込み関数 // std::simdには対応するマッピングがない auto addsub(simd<float> a, simd<float> b) -> simd<float> { return static_cast<simd<float>>(_mm256_addsub_ps(static_cast<__m256>(a), static_cast<__m256>(b))); }
ただし、この変換はstd::simd
のサイズがネイティブのSIMDレジスタサイズに等しい場合にのみ正しく動作します。ネイティブのSIMD幅と異なる場合はそれを考慮しなければならず、ネイティブのSIMD幅を超え複数のレジスタにまたがるサイズの場合はこのような変換は利用できません。
その場合は、大きな幅のstd::simd
値をネイティブのSIMDレジスタサイズにマッチするサイズで分割して順番に組み込み関数を呼び出して、その結果をstd::simd
に格納するようなことをすることになります。
// AVXを使用する環境で、ネイティブSIMDレジスタサイズは256(floatx8)とする // simd<float, 16>はネイティブSIMD幅の倍のサイズ auto addsub(simd<float, 16> a, simd<float, 16> b) -> simd<float, 16> { // ネイティブのレジスタサイズごとに分割 auto [lowA, highA] = simd_split<simd<float>>(a); auto [lowB, highB] = simd_split<simd<float>>(b); // 分割した部分ごとに組み込み関数を呼び結果を取得 auto resultLow = simd<float>(_mm256_addsub_ps(static_cast<__m256>(lowA), static_cast<__m256>(lowB))); auto resultHigh = simd<float>(_mm256_addsub_ps(static_cast<__m256>(highA), static_cast<__m256>(highB))); // 分割して処理した結果をsimd<float, 16>に再結合する return simd_concat(resultLow, resultHigh); }
この例はネイティブのSIMDレジスタサイズの倍のstd::simd
値に対してのみ動作し、異なるサイズの場合はそれに合わせて書き直す必要があるほか、ネイティブのSIMDレジスタサイズの整数倍とならないようなサイズの場合はそのための対応が必要となります。そのようなコードを書くことは難しくはなくほとんど典型的なコードとなると思われますが、それでも冗長であり、汎用的なソリューションでは技巧的なコードを書くことになります。
ここでやるべきことは、std::simd
値の引数をネイティブのSIMDレジスタサイズで分割し、その部分ごとに組み込み関数を呼び出し、その結果をstd::simd
値に再びまとめる、ということです。やるべきことのほとんどは典型的な処理であり、個別ユーザーそれぞれにそれを書かせる代わりに、この一連の手順を抽象化した一般的なメカニズムとして提供することができます。この提案ではそれを、simd_invoke()
という関数テンプレートとして提案しています。
template<typename Fn, typename... Args> auto simd_invoke(Fn&& fn, Args&&...);
fn
はネイティブのSIMDレジスタサイズのstd::simd
値を受け取って組み込み関数を呼びだす部分を指定する呼び出し可能なものです。例えば、先ほどの_mm256_addsub_ps
を呼び出す関数の場合は次のようになります
// AVXレジスタ1つ分の_mm256_addsub_psを処理する inline auto native_addsub(simd<float> lhs, simd<float> rhs) { auto nativeLhs = static_cast<__m256>(lhs); auto nativeRhs = static_cast<__m256>(rhs); return simd<float>(_mm256_addsub_ps(nativeLhs, nativeRhs)); }
これを利用して、先ほどのようなネイティブのSIMDレジスタサイズを超える幅のstd::simd
値で組み込み関数を呼びだすコードは次のように書き直せます
auto addsub(simd<float, 32> x, simd<float, 32> y) { return simd_invoke(native_addsub, x, y); }
入力のstd::simd
値をネイティブSIMD幅ごとに分割して組み込み関数を呼びだす部分と、その処理結果を再統合する部分はsimd_invoke()
が勝手にやってくれています。それによって、ユーザーは単位SIMD幅のstd::simd
値に対して何をするかだけを用意する(上記native_addsub()
のように)だけでSIMD幅を気にせずにstd::simd
を使って組み込み関数を呼びだすことができるようになっています。
先ほどの例の直接比較
現在 | この提案 |
---|---|
auto addsub(simd<float, 16> a, simd<float, 16> b) -> simd<float, 16> { auto [lowA, highA] = simd_split<simd<float>>(a); auto [lowB, highB] = simd_split<simd<float>>(b); auto resultLow = simd<float>(_mm256_addsub_ps(static_cast<__m256>(lowA), static_cast<__m256>(lowB))); auto resultHigh = simd<float>(_mm256_addsub_ps(static_cast<__m256>(highA), static_cast<__m256>(highB))); return simd_concat(resultLow, resultHigh); } |
auto addsub(simd<float, 16> a, simd<float, 16> b) { auto do_native = [](simd<float> lhs, simd<float> rhs) { return simd<float>(_mm256_addsub_ps( static_cast<__m256>(lhs), static_cast<__m256>(rhs))); }; return simd_invoke(do_native, x, y); } |
また、ネイティブのSIMDレジスタサイズの整数倍とならないような入力に対してより適切に組み込み命令を選択することもできます
auto addsub(simd<float, 19> x, simd<float, 19> y) { // AVXでfloat 19要素の場合、8, 8, 3 と分割して処理する // 入力のsimdのサイズごとに適切な組み込み関数を選択する auto do_native = []<typename T, typename ABI>(basic_simd<T, ABI> lhs, basic_simd<T, ABI> rhs) { constexpr auto size = basic_simd<T, ABI>::size; if constexpr (size <= 4) { // 4要素(128bit)SIMD return simd<float, size>(_mm_addsub_ps(static_cast<__m128>(lhs), static_cast<__m128>(rhs))); } else { // 8要素(256bit)SIMD return simd<float, size>(_mm256_addsub_ps(static_cast<__m256>(lhs), static_cast<__m256>(rhs))); } }; return simd_invoke(do_native, x, y); }
他にも、simd_invoke()
はNTTP値で指定することでより狭いレジスタ幅で処理をするようにできたり、インデックスを同時に渡すsimd_invoke_indexed()
が用意されていたりします
// より狭いレジスタ幅で呼び出しを行う例 auto addsub(simd<float, 32> x, simd<float, 32> y) { auto do_native = [](simd<float, 4> lhs, simd<float, 4> rhs) { return simd<float, 4>(_mm_addsub_ps(static_cast<__m128>(lhs), static_cast<__m128>(rhs))); }; // NTTPでレジスタサイズを指定 return simd_invoke<4>(do_native, x, y); } // 組み込み関数によって何か特殊なメモリストアを行う例 // _mm256_special_store_psは実際には存在しない auto special_memory_store(simd<float, 32> x, float* ptr) { // ptrの領域に適切に出力していくために、オフセットが必要 auto do_native = [=]<typename T, typename ABI>(basic_simd<T, ABI> data, auto idx) { (_mm256_special_store_ps(ptr + idx, static_cast<__m256>(data))); }; // 分割ごとの通し番号を同時に渡す simd_invoke_indexed(do_native, x); }
P2930R0 Formatter specializations for the standard library
標準ライブラリのクラス型について、std::format
で直接文字列化できるようにする提案。
C++20でstd::format
が追加されると同時に基本型のためのフォーマット指定構文が導入され、C++23ではstd::print
が追加されるとともに任意のrange
型やstd::tuple
などがサポートされるようになりました。しかし、依然としてその他の多くの標準ライブラリのクラス型にはstd::format
のサポートがなく、効率的に基本型へ変換できない場合は文字列化するためには従来のストリーム出力(<<
)に頼るしかありません。
この問題は以前から認識されており、P1636R2で同様の提案がなされLEWGでの設計合意を得ていましたが、著者の方と連絡が取れなくなったため議論は停止していました。この提案は、P1636R2をベースに、C++23におけるstd::formatter
の改善なども盛り込みながら、より多くの標準ライブラリクラス型をフォーマット可能にすることを目指すものです。
この提案でフォーマットサポート追加を提案しているのは次のものです
std::bitset
std::byte
std::complex
std::error_category
std::error_code
std::error_condition
std::sub_match
これ以外のものはそれぞれ簡単には解決できない問題があるとしてここでは提案されていません(ただし、std::filesystem::path
は別の提案で提案されています)。
- P2845R0 Formatting of
std::filesystem::path
- WG21月次提案文書を眺める(2023年05月) - P1636R2 Formatters for library types
- P2930 進行状況
P2931R0 WG21 February 2023 Meeting Record of Discussion
2023年6月のWG21全体会議(Varna会議)の議事録
P2937R0 Freestanding: Remove strtok
フリースタンディングライブラリ機能として指定するものから、strtok
を外す提案。
strtok
はP2338R4によってC++26のWDに対してフリースタンディング機能であると指定されました。
strtok
は実際にはその動作のためにグローバルストレージを使用しており、スレッドセーフであることが保証されません。そのためP2338の初期のリビジョンには含まれていなかったのですが、C2X(Cの次期バージョン)に対してstrtok
がフリースタンディングであると指定されたことで、P2338も最終的にはそれに追随しました。
しかしその後、C2XのCD2(comitee draft)の段階でstrtok
のフリースタンディング指定は解除されたため、この提案はC++でも同様にstrtok
をフリースタンディングでは無くすことを提案しています。
- P2338R4 Freestanding Library: Character primitives and the C library - WG21月次提案文書を眺める(2023年02月)
- P2937 進行状況
P2940R0 switch for Pattern Matching
パターンマッチングの構文として、switch
を拡張する方向性についての提案。
現在のパターンマッチングの議論においては、swicth
を再利用するのではなくinspect
という新しいキーワードによってパターンマッチングのための領域を導入する方向性で議論が進んでいます。これは主に次の2つの理由によっています
swicth
構文が古いswitch
文なのかパターンマッチ構文なのかを判別するのが難しい- 古い
swicth
とパターンマッチでは機能や意味論が異なるためそれを教育する際の懸念がある
この提案は、どちらの問題も解決可能であるとして、switch
構文を拡張する形でパターンマッチングを導入しようとするものです。
1つ目の問題の解決として、この提案ではswitch
キーワード直後の値を指定する部分を()
ではなく[]
を使用することで区別することを提案しています。
// 現在のswitch文 switch (a) { ... } // この提案によるパターンマッチ構文、基本形 switch [a] { ... }; // この提案によるパターンマッチ構文、aとbの両方でマッチングする switch [a, b] { ... };
これによって導入されるswitch
は式となり、通常のswitch
同様の構文(case
やdefault
ラベルによって)でマッチングを記述でき、また現在提案中のパターンマッチング構文と同様の構文(=>
など)を用いてマッチングし値を返すことができます(case
やdefault
ラベルによるマッチングの場合は値を返すことができない)。ただしこのために、[]
によるswitch
では末尾に;
が必要とされます。
この提案のswitch
式はフォールスルーに関してswitch
文と異なるデフォルトとすることを提案しており、明示的にOR条件を記述しない限りフォールスルーしないようにしています。
switch文 | switch式 |
---|---|
auto some_value = 2; switch (some_value) { case 1: ... break; case 2: ... // break忘れ、フォールスルーしてしまう case 3: ... break; ... } |
auto some_value = 2; switch [some_value] { case 1: ... break; case 2: ... // break忘れ、フォールスルーしない case 3: ... break; ... }; |
このフォールスルーに関して以外は、新しいswitch
式は現在のswitch
文の機能を包含しています。これによって、現在のswitch
文はパターンマッチのより制限された場合と見ることができるようになり、教育において2つを区別する必要は無くなります(2つ目の問題を解決します)。
提案より、その他の例
auto some_value = string("hi"); // 文字列のマッチング switch [some_value] { case "hi": // handle "hi" case "bye": // handle "bye" default: // handle all else }; auto some_value2 = Point(12, 13); // 現在のパターンマッチング構文によるマッチング switch [some_value2] { case [0, 0]: // handle point at origin case [0, _]: // handle x at origin case [_, 0]: // handle y at origin ... }; auto some_value3 = true; // 値を返すswitch式、戻り値型は推論され、const char*になる auto result = switch [some_value3] { true => "yes"; false => "no"; }; // 値を返すswitch式、戻り値型はstd::string auto result = switch [some_value] -> std::string { true => "yes"; false => "no"; }; enum class Op { Add, Sub, Mul, Div }; // caseラベルと=>を混合させることで、値を返さないパターンを簡易に記述できる Op parseOp(Parser& parser) { return switch [parser.consumeToken()] { '+' => Op::Add; '-' => Op::Sub; '*' => Op::Mul; '/' => Op::Div; case [[noreturn]] let token: { std::cerr << "Unexpected: " << token; std::terminate(); } }; }
これらの例が示すように、パターンマッチングサポートのためにswitch
を拡張することは可能でありかつ自然な拡張となり、導入キーワードやcase
記述などを再利用することで言語の変更を抑えることができ、現在のswitch
の自然な拡張となることで初心者が学ぶべきことも減少します。
P2941R0 Identifiers for Pattern Matching
パターンマッチング中で使用される識別子を区別可能なパターンマッチング構文の提案。
現在議論中のパターンマッチング構文においては、マッチングを記述するところに識別子(なんらかの名前)を使用してマッチングを行うことができます。この時問題となるのは、そのように使用されている識別子が型なのか変数なのかはたまた新しく導入されたものなのかわからない場合があるという点です。
static constexpr int zero = 0, one = 1; int v = 42; inspect (v) { zero => { std::cout << zero; } // zeroはどれ? };
他言語におけるパターンマッチングを見てみてもこの問題に正解はないようです。しかし、次の2つの理由により、C++ではこのことが深刻な問題となる可能性があります
このことは現在ではあまり問題とはなりませんが、パターンマッチング構文の柔軟さと合わさると複雑な問題を引き起こします。
パターンマッチング構文内でx
という識別子が表れているとき、それは次の3つのいずれかであるはずです
x
はパターンマッチング構文内で新しく導入された変数もしくはバインディング(マッチング対象の値のサブオブジェクトへの参照)x
は比較対象となる外部の変数x
は型
これに対して、現在のパターンマッチング提案は次のようになっています
- P1371R3
- デフォルトではバインディングを作成する
- 外部の変数を比較対象とする時は、
case
を使用する - 型名は
<type>
のように記述する
- P2688(P1371の将来バージョン)
- バインディングの作成は
let
で行う - 外部変数は単なる式として扱われ、識別子名のデフォルトは外部変数となる
- 型についてはそのまま
- バインディングの作成は
- P2392(
is as
を使用する提案)- 識別子が
is as
の左側にある場合、バインディングが作成される - 外部変数は
is
の右側にくる - 型も
is as
の右側にくる
- 識別子が
ただし、どちらのアプローチにおいても式と型名を同じ構文で指定できてしまうため、型のマッチング記述に問題があります。
P1371R3のAlternative Pattern(パターンマッチング対象の値を直接マッチングする代わりに、そこから何かしら値を取り出してそれによってマッチングする。std::varint
のマッチングなど)
void sample1(auto some_value) { constexpr auto size = 13; inspect (some_value) { <size> => // マッチングには定数13が使用される }; } void sample2(auto some_value) { using size = int; inspect (some_value) { <size> => // マッチングにはint型が使用される }; }
一応この場合<>
の中に指定する式は定数式である必要はあります。
P2392の場合
void sample1(auto some_value) { constexpr auto size = 13; inspect (some_value) { is size => // some_valueが13に等しい時にマッチング }; } void sample2(auto some_value) { using size = int; inspect (some_value) { is size => // some_valueの型がintの時にマッチング }; }
同じ構文によって異なるマッチングができてしまうことによって、ある時点で識別子の意味が変化するとパターンマッチングの意味もまた静かに変化してしまいそれに気づけない可能性があります。より大規模な関数ではこのような状況が容易に発生することが予想されます。
また逆に、同じ名前で別の種類のエンティティがパターンマッチングのスコープに導入されている場合も考えられます。マッチングの記述方法によっては問題とならない場合もありますが、上記のように式も型も書ける構文でそのような識別子を使用してしまうとコンパイルエラーとなります。
いずれにせよ、この型マッチング記述の曖昧さの問題によってパターンマッチングはプログラマの意図通りに動作せず、さらに悪い場合は動作しているように見えてしていないという問題に頭を悩ませることになる未来があり得ます。
この問題の根本的原因はパターンマッチングによって導入されたわけではありませんが、今日のコードで問題となる場合はほとんどありません。なぜなら、識別子名が型であるか変数であるかが競合する場所というのは言語機能としては存在していないからです。NTTPと型を受け取る関数テンプレートのようにそのような場所を作り出すことはできますが、これを使用する場合は何らかのライブラリAPIとして使用することになり、それは言語機能ではありません。
パターンマッチングの場合は言語機能で識別子名の種類が競合するコンテキストが表れてしまっています。現在のC++の式は、同じ名前の型や変数の有無で意味が切り替わることはなく、コンパイルエラーとなるはずです。パターンマッチングは現在の言語の振る舞いより悪くなることはなくむしろ良い振る舞いをしなければなりません。そうならない場合、新しい罠を生み出してしまい、それを回避するためのガイドラインを普及させることになります。
また、パターンマッチで外部変数を使用する場合の構文として、マッチングとその結果あるいはサブオブジェクトの代入を同時に行うものが考えられます。例えば、構造化束縛を拡張して既存の変数に代入するようなもの(std::tie()
のような)が考えられます
some_t a,b;
// 構造化束縛構文を拡張して既存の変数に代入するようにしたもの
[a,b] = something;
これはパターンマッチングと関係なく有用であり導入可能である可能性がありますが、同様にパターンマッチングにおいても有用である可能性があります。その場合、パターンマッチングで現れる識別子の種別として代入に使用される既存変数名が追加されます。
さらに、パターンマッチングにおいてバインディングを作成する場合、その不変性を制御できることが望ましいことは明らかです。現在のパターンマッチング提案ではこれはマッチング対象のオブジェクトのconst
性とその伝播の通常のルールによって制御されますが、それは次のような理由から完璧ではありません
- パターンマッチングの全ての分岐パスで同じ使い方をするわけではなく、分岐によって
const
でよかったり変更が必要だったり変化しうる。マッチング全体で一律にconst
であるかそうでないかを指定させることは、全体として最適なconst
性を表現できなくなる - ポインタやビューなど参照セマンティクスを持つものの場合、その
const
性は参照先のconst
を意味しない。現在のC++でもそのようなオブジェクトは広く使用されており、マッチングにおけるバインディングそれぞれで個別にconst
性を制御しない方法は実用的ではない
これらの検討の結果、パターンマッチングにおいて現れる識別子の種類としては次の5つが考慮されることになります
x
はパターンマッチング構文内で新しく導入された、観測専用(const
な)変数もしくはバインディングx
はパターンマッチング構文内で新しく導入された、変更可能な変数もしくはバインディングx
は比較対象となる外部の変数x
はマッチング結果の代入対象となる外部の変数x
は型
パターンマッチング構文においては、これらのパターンを区別しながら識別子を使用できることが望ましいです。
この提案では、その解決のための構文として次のようなものを提案しています。
識別子x
があったとき
ただし、値マッチングに常に==
を要求するのは冗長であり見づらくなる可能性があるほか、単一の識別子がいつも型名とみなされるのも混乱を招く可能性があります。そこで、次のような制限を加えています
- 最後のパターンの場合、
==
を省略できる - 最後のパターンは常に型名マッチングではない
- 最後のパターンに現れる識別子は型名とみなされない
最後のパターンというのは、パターンマッチングにおける一つのパターンのマッチングにおいて、パターンの外側から内側へ再帰的にマッチングされる際の最も内側のパターン(つまり一番最後にマッチングがチェックされるパターン)のことです。この制限によって、パターンマッチングに現れる単一の識別子のデフォルトは==x
となり、型マッチングはx __
のように記述することになります。
struct size{...}; // #1 size size; // #2 inspect (...) { size __ => ... // 型マッチング(sizeは型名#1) size => ... // 値のマッチング(sizeは変数名#2) [size, size] => ... // これも値マッチング(#2) [==size, ==size] => ... // 上と同じ意味 size size => ... // size型(#1)のsize値(#2)とのマッチング size (==size) => ... // 上と同じ意味 };
==
が必要とされる例
struct Size{...}; Size size; // #1 inspect (...) { // size [w=, h=] => ... // error、sizeは型名ではない // Size size[w=, h=] => ... // error、sizeは型名ではない(最後のパターンではないため変数名とみなされていない) ==size[w=, h=] => ... // ok、::size(#1)とのマッチング、そのサブオブジェクトをw, hにバインディング };
この場合、最後のパターンは[]
の中のw=, h=
(の両方)になるため、それ以外のところで==
を省略するとその識別子は型名だとみなされ、それによって最初の2つの例はエラーになります。
同様に、型の一致は最後のパターンに来ないようにする必要があります。
struct Size{...}; Size size; struct Rectangle { int x; int y; Size size; }; Rectangle rect = ...; inspect (rect) { // [__, __, size __] => ... // error、sizeは型名ではない [__, __, ==size __] => ... // ok、明示的な値マッチング [__, __, size] => ... // ok、最後のパターンのため値マッチングとみなされる // [__, __, Size] => ... // error Sizeは型名(最後のパターンのためSizeは変数名とみなされている) [__, __, Size __] => ... // ok、最後のパターンではないためSizeは型名とみなされる [__, __, Size [w=, h=]] => ... // ok、最後のパターンではないためSizeは型名とみなされる };
その他の例
int x = 5; inspect (...){ x => // ::x(変数)とマッチング ==x => // 上と同じ意味(明示的) x= => // バインディングの導入、::xを隠蔽 x=x => // バインディングの導入 + ::xとのマッチング x=(==x) => // 上と同じ意味(明示的) int x => // int型かつ::xとマッチング px= (int x=x) => // int型かつ::xとマッチング、マッチングした場合その値はint型の値xにバインドされ(値は::xと一致する)、多態的型にバインドされたpxにさらにバインドする int &x=x => // int型かつ::xとマッチング、マッチングした場合その値はint型の参照xにバインドされる(値は::xと一致する) ... };
- P2688R0 Pattern Matching Discussion for Kona 2022 - WG21月次提案文書を眺める(2022年10月)
- P1371R3 : Pattern Matching - WG21月次提案文書を眺める(2020年09月)
- P2941 進行状況
P2944R0 Comparisons for reference_wrapper
reference_wrapper
に比較演算子を追加する提案。
reference_wrapper<T>
には比較演算子が定義されていませんが、reference_wrapper<T>
はT&
に暗黙変換が可能であり、比較を行う際はADLにおいてT
の名前空間が探索候補になることから、T
に定義されている比較演算子を使うことができます。ただし、これには制限もあり比較演算子の定義のされ方などによって比較が可能かが変化します。
reference_wrapper<T>
のT
の種類と演算子の定義のされ方、比較の仕方によって比較が可能かは次の表のようになります。
T |
ref(t) == ref(t) |
ref(t) == t |
---|---|---|
組み込み型 | ✅ | ✅ |
クラス(テンプレート)で== はメンバで定義 |
❌ | ✅ |
クラスで== は非メンバで定義 |
✅ | ✅ |
クラステンプレートで== はhidden friendで定義 |
✅ | ✅ |
クラステンプレートで== は非メンバ関数テンプレートで定義 |
❌ | ❌ |
std::string_view |
❌ | ✅ |
reference_wrapper<T>
を介してT
に定義された比較演算子の上で比較を行いたい場合、そのままだとこの表にあるようにできたりできなかったりします。
reference_wrapper<T>
を介してT
の比較演算子によって比較したいユースケースがあり、それはreference_wrapper
を使用する理由と関連しています。
// valueと等しいかをチェックする述語生成ラッパ inline constexpr auto equals = [](auto&& value) { return [value=FWD(value)](auto&& e){ return value == e; }; };
これはvalue
を値でキャプチャするためvalue
がコピーの重い型だと非効率になりますが、ここで適応的に参照キャプチャしようとすると実装が複雑化します。この時、このラッパの入力にreference_wrapper
を用いることによって、使用者の意思で明示的に参照キャプチャするかどうかを切り替えることができます。
if (std::ranges::any_of(v, equals(0))) { // ... } if (std::ranges::any_of(v, equals(std::ref(target)))) { // ... }
これは先ほどの表のようにtarget
の型と比較演算子の定義のされ方によって動作したりしなかったりします。この提案の目的は、これが常に動作するようにすることにあります。
この提案による解決は単純で、reference_wrapper<T>
に対して直接的に比較演算子を定義することで、ADLに頼らずにT
の比較演算子を利用できるようにします。
namespace std { template<class T> class reference_wrapper { public: ... // 追加される比較演算子 friend constexpr bool operator==(reference_wrapper, reference_wrapper); friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper); }; }
これらの比較演算子は、reference_wrapper<T>
が参照しているT
の値で直接対応する比較を行うことによって比較を実行するため、現在のreference_wrapper<T>
のように比較できたりできなかったりすることは無くなります。
また、比較演算子の導出によってT
でそれが可能ならば全ての種類の比較が可能となります。
P2945R0 Additional format specifiers for time_point
<chrono>
のtime_point
型のフォーマット指定を追加する提案。
C++20で<chrono>
ライブラリは拡張され、カレンダー表現やタイムゾーンのサポートなどと共にstd::format()
によるフォーマット対応もなされました。それによってtime_point
型の値(時刻の値)はそのままstd::format()
で文字列化することができ、またフォーマット指定を添えることで文字列化のされ方を制御することができます。
#include <chrono> #include <format> #include <iostream> using namespace std::chrono; int main() { // system_clockのtime_point値(現在時刻)の取得 auto now = system_clock::now(); // time_point値のフォーマット出力 std::cout << std::format("{}\n", now); std::cout << std::format("{:%Y年%m月%d日 %H時%M分%S秒}\n", now); std::cout << std::format("{:%D %T}\n", now); std::cout << std::format("{:%F %T %Z %z (%Ez)}\n", now); }
出力例
2023-09-05 12:28:41.739583722 2023年09月05日 12時28分41.739583722秒 09/05/23 12:28:41.739583722 2023-09-05 12:28:41.739583722 UTC +0000 (+00:00)
この提案は、現在のchrono
のフォーマット指定に欠けているフォーマット方法を追加しようとするものです。
この提案が問題視しているのは、上記例にも表れているように秒の単位以下の出力精度を制御する方法が提供されていないことです。%S
は秒単位かつミリ秒未満は10進小数で出力しますが、この時ミリ秒未満をどこまで出力するのかあるいは出力しないということを指定する方法が提供されていません。%T
は%H:%M:%S
の省略指定であるため同様の問題があります。これによって、time_point
値を秒単位で出力、ミリ秒単位(小数点以下3桁)だけ出力、などの指定は少し遠回りをする必要があります。
#include <chrono> #include <format> #include <iostream> using namespace std::chrono; int main() { // system_clockのtime_point値(現在時刻)の取得 auto now = system_clock::now(); // 秒単位まで出力 std::cout << std::format("{:%H:%M:%S}\n", time_point_cast<seconds>(now)); // ミリ秒単位(小数点以下3桁)まで出力 std::cout << std::format("{:%H:%M:%S}\n", time_point_cast<milliseconds>(now)); // マイクロ秒単位(小数点以下6桁)まで出力 std::cout << std::format("{:%H:%M:%S}\n", time_point_cast<microseconds>(now)); }
出力例
12:42:14 12:42:14.700 12:42:14.700612
この提案では、これに対して%.nS
のようなフォーマット指定を提案しており、n
で秒単位の出力精度(桁数)を指定します。
#include <chrono> #include <format> #include <iostream> using namespace std::chrono; int main() { auto now = system_clock::now(); // 秒単位まで出力 std::cout << std::format("{:%H:%M:%.0S}\n", now); // ミリ秒単位(小数点以下3桁)まで出力 std::cout << std::format("{:%H:%M:%.3S}\n", now); // マイクロ秒単位(小数点以下6桁)まで出力 std::cout << std::format("{:%H:%M:%.6S}\n", now); }
同時に、%T
に対しても%.nT
のように同様に秒単位の出力桁数を指定するフォーマット指定を提案しています。
#include <chrono> #include <format> #include <iostream> using namespace std::chrono; int main() { auto now = system_clock::now(); // 秒単位まで出力 std::cout << std::format("{:%.0T}\n", now); // ミリ秒単位(小数点以下3桁)まで出力 std::cout << std::format("{:%.3T}\n", now); // マイクロ秒単位(小数点以下6桁)まで出力 std::cout << std::format("{:%.6T}\n", now); }
このフォーマット指定の利点は以下の3点が主張されています
- これがない場合、
time_point
値の精度を変換するための冗長なキャスト(time_point_cast
)を記述しなければならず、不便 - 提案しているフォーマット指定では、実際の
time_point
値の精度を気にする必要がないことを保証しているtime_point
値の精度によらず、一貫して同じ出力を得られる
std::optional
やrange
などの要素となっている時のフォーマット時にフォーマット指定を再利用できる- 単に入れ子にすれば良い
time_point_cast
で回避する場合、追加の変換(.transform()
やviews::transform
)が必要になり、これは場合によって安全性の問題を引き起こす
また、多言語におけるこのtime_point
値のフォーマットに対応するフォーマット指定を見てみると、次のような明確なコンセンサスが確認できます
%S
は秒数を00 ~ 59
の2桁の整数としてフォーマットする%s
は秒数をエポックからの整数値としてフォーマットする
現在のC++は残念ながらどちらも満たしていません(%s
指定は存在しない)。そこでこの提案では、%s
指定をこの意味論に沿った効果を持つフォーマット指定として追加することも提案しています。
#include <chrono> #include <format> #include <iostream> using namespace std::chrono; int main() { auto now = system_clock::now(); // エポックからの経過秒数(or経過単位時間カウント値)の出力 // 現在 std::cout << std::format("{:%Q}\n", now.time_since_epoch()); // 最大精度 std::cout << std::format("{:%Q}\n", time_point_cast<seconds>(now).time_since_epoch()); // 秒単位 // この提案 std::cout << std::format("{:%s}\n", now); // 最大精度 std::cout << std::format("{:%.0s}\n", now); // 秒単位 }
出力例
1693915931163502515 1693915931 1693915931163502515 1693915931
まとめると変更は次のようになります
%s
を追加して、time_point
値をその精度単位でエポックからのカウント数を整数値でフォーマットする%S %s
の両方で、プリフィックスとして出力精度(秒未満の桁数)を受け付けるようにする%.nS %.ns
%T
を%S
と同様に拡張%f
を追加して、%0S%.nf
が%.nS
と同じ意味になるようにする%Q %q
をtime_point
でも機能するようにする- 現在は
duration
でのみ有効
- 現在は
この提案ではさらに踏み込んで、%S
の出力を他言語と一貫性のあるものに修正することを提案しています。それが受け入れられる場合、%S %s
の両方で%.nS %.ns
の指定の代わりに小数点以下の部分を表すフォーマット指定として%f
を追加することを提案しています。
#include <chrono> #include <format> #include <iostream> using namespace std::chrono; int main() { auto now = system_clock::now(); // `%S`の出力修正が受け入れられる場合の提案 // 秒単位まで出力 std::cout << std::format("{:%H:%M:%S}\n", now); // ミリ秒単位(小数点以下3桁)まで出力 std::cout << std::format("{:%H:%M:%S%.3f}\n", now); // マイクロ秒単位(小数点以下6桁)まで出力 std::cout << std::format("{:%H:%M:%S%.6f}\n", now); // 秒単位まで出力 std::cout << std::format("{:%T%}\n", now); // ミリ秒単位(小数点以下3桁)まで出力 std::cout << std::format("{:%T%.3f}\n", now); // マイクロ秒単位(小数点以下6桁)まで出力 std::cout << std::format("{:%T%.6f}\n", now); // エポックからの経過秒数(or経過単位時間カウント値)の出力 std::cout << std::format("{:%s}\n", now); // 最大精度 std::cout << std::format("{:%.0s}\n", now); // 秒単位 }
こちらの提案の場合変更は次のようになります
%S
は秒数を00 ~ 59
の2桁の整数としてフォーマットするように変更%H %M
と同様になる
%s
を追加して、time_point
値をエポックからの秒数を表す整数値としてフォーマットする%f
を追加して、time_point
値の出力時にその最大精度でフォーマットする%.nf
のようにして、精度を指定することもできる
%Q %q
をtime_point
でも機能するようにする- 現在は
duration
でのみ有効
- 現在は
こちらの提案は出力結果に関して破壊的変更となり、現在の%S
は%S%.f
、%T
は%T%.f
が対応します。
%S
の修正を含む提案を提案2、%S
を現状維持する提案を提案1とすると、2023年7月09日15時40分34秒(+0.295314673秒、UTC)の時刻を保持するtime_point
値tp
に対して同じ出力を得るためのフォーマット指定には次のような差が生じます
提案2 | 期待する出力 | 提案1 |
---|---|---|
std::format("{:%s%9f}", tp) |
1688830834295314673 |
std::format("{:%s}", tp) |
std::format("{:%s}", tp) |
1688830834 |
std::format("{:%.0s}", tp) |
std::format("{:%H:%M:%S}", tp) std::format("{:%T}", tp) |
15:40:34 |
std::format("{:%H:%M:%.0S}", tp) std::format("{:%.0T}", tp) |
std::format("{:%H:%M:%S%.3f}", tp) std::format("{:%T.%3f}", tp) |
15:40:34.295 |
std::format("{:%H:%M:%.3S}", tp) std::format("{:%.3T}", tp) |
std::format("{:%H:%M:%S%.6f}", tp) std::format("{:%T.%6f}", tp) |
15:40:34.295314 |
std::format("{:%H:%M:%.6S}", tp) std::format("{:%.6T}", tp) |
この提案では提案2を押していますが、破壊的変更となるため受け入れられない場合のために提案1を用意しています。
LEWGの最初のレビューでは、%S
の修正を受け入れることに弱いコンセンサスがあり、その場合実装はC++20/23モードまで遡って修正を適用することにコンセンサスがありました。
P2946R0 A flexible solution to the problems of noexcept
noexcept
よりも弱い無例外指定である[[throws_nothing]]
の提案。
関数に対するnoexcept
指定はその関数が例外を投げないことを指定し、例外が投げられた場合はプログラムを終了させます。これは、std::vector
等のコンテナの挿入操作などにおいて強い例外保証とムーブコンストラクタの効率性を両立させるために導入されました。ムーブはムーブ元の状態を変更してしまうため、ムーブコンストラクタが例外を投げるとムーブ元の状態を元に戻せなくなり、強い例外保証を守ることができなくなります。そのため、ムーブコンストラクタが例外を投げないことをコンパイル時に調べてその場合にのみムーブによって要素を構築するために、コンストラクタ(関数)が例外を投げないことの表明と関数が例外を投げないことのチェックをnoexcept
指定子と演算子によって行えるようにしています。
その後これを標準ライブラリの他の関数等にも適用する際のルールとしてLakos Ruleが整備されました。これは関数が事前条件を持つかによって2種類に分類してその上でnoexcept
を指定すべきかを決定するシンプルなルールです。簡単には次のようなものです
- 関数は事前条件を持たず(広い契約を持つ)、例外を投げない場合、関数を
noexcept
指定する - 関数は事前条件を持っている(狭い契約を持つ)か、事前条件を満たして呼び出された場合にも例外を投げる可能性がある場合、関数は
noexcept
指定するべきではない
単純なvector
実装における例
template <class T, class A> class vector { // ... constexpr size_type size() const noexcept; // 広い契約, 例外を投げない constexpr reference at(size_type); // 広い契約, 例外を投げうる constexpr reference operator[](size_type); // 狭い契約, 例外を投げない constexpr reference front(); // 狭い契約, 例外を投げない };
Lakos Ruleは効果的であり、理論的にも実践的にも強い裏付けを持っています(下位互換性のある拡張を可能にしたり、より広いインターフェースに適合することができるなど)が、このルールに従わない理由として次の2つのようなものが明らかになっています
- 多くの状況では
noexcept
関数の呼び出しに伴って生成されるコードが少なくなる。そのため、コード生成を改善するためにnoexcept
をとにかく使用したい需要がある - C++の規格書において、「Throws: nothing」と指定されているものと
noexcept
の区別が明確ではない
また、その他の動機として、noexcept
関数が例外を投げた場合に即終了しないでほしい場合があり、単なる事前条件のチェックのために例外を用いたいなどの需要もあるようです。
この2つの需要に対しては、関数にとにかくnoexcept
を付けるという動機が生まれてきます(つまり、Lakos Ruleを無視する)。しかし、ある時点で関数にnoexcept
を付加してしまうと将来のバージョンで下位互換性を壊すことなく削除することができなくなるため(関数にnoexcept
が付加されている場合、その関数を例外を投げる可能性のある値を受け入れるように拡張できなくなる)、どうしてもコンパイル時にその関数のnoexcept
性が重要になる説得力のある理由がない限りはLakos Ruleに違反することは望ましくありません。
このような現状に対してこの提案は、現在のnoexcept
及びLakos Ruleを維持しながらも、これらのような現在のnoexcept
では満たすことのできないいくつかの需要に対処するために、[[throws_nothing]]
という無例外を表明する属性を追加しようとするものです。
この提案の目的は現在のnoexcept
では対応しきれない次ような需要に対処することです
- より小さなコードサイズ
- 生成されるコードサイズの削減の恩恵を最も受けるのはメモリに制約のある環境
- 組み込み環境において例外が使用しやすくなる可能性がある
- 正常なシャットダウン
- エラー(例外)が発生したからといって即終了されることは受け入れられない環境がある
- 終了の前にリソースを解放したりログをとったりデータを保存したりする必要がある
- 終了しない例外
- 決して終了しないプログラムが存在する
- テストドライバーはその一例だが、関数の事前条件のテストにおいては意図的な失敗も含めてテストをするが、ここで例外が投げられプログラムが終了する場合テストは不可能になる
[[throws_nothing]]
は関数宣言に指定する属性であり、その関数の事前条件が満たされていれば例外を投げないことを指定します。しかしこの指定はコンパイル時に検出する方法がなく、noexcept
演算子はこれを無視し関数型にも現れません。
[[throws_nothing]] void g1(int); static_assert(noexcept(g1(0)) == false); [[throws_nothing]] void g2(int) noexcept; static_assert(noexcept(g2(0)) == true); [[throws_nothing]] void g3(int) noexcept(false); static_assert(noexcept(g3(0)) == false); void g4(int); static_assert(std::is_same_v<decltype(g1), decltype(g4)>);
[[throws_nothing]]
はnoexcept
演算子等で検出できないため、それによってコンパイル時に分岐することもできなくなり、これによって関数の動作が以前に有効だった入力に対して変更されない限りは、後からこの指定を削除することができます。
[[throws_nothing]]
指定された関数が例外を投げることで終了する場合にプログラムが終了するか(あるいは例外が通常のように伝播するか)は実装定義とされます。推奨される実装としてはその動作をユーザーが指定できることが望ましいとしています。これによって、例外発生時に終了することを選択した実装ではnoexcept
同様にコードサイズ削減効果が期待でき、終了しない実装では正常なシャットダウンや事前条件テストなどを例外を用いて行うことができるようになります。
また、標準ライブラリ実装においては現在のnoexcept
同様に標準で指定されていなくても[[throws_nothing]]
を指定することを許可します。これによって、「Throws: nothing」と指定されている関数に対して[[throws_nothing]]
属性を指定することができ、現在のnoexcept
と同等の効用を得ながらもより柔軟な運用が可能となります。前述のように、後から削除する場合でも問題なく行うことができます。
noexcept
及び何も指定しない場合との効用の比較表
指定なし | noexcept |
[[throws_nothing]] |
|
---|---|---|---|
関数の自己ドキュメント | no | yes | yes |
コード生成へのヒント | no | yes | 終了する場合yes |
予期しない例外時の終了 | no | yes | 終了する場合yes |
広い契約に最適 | yes | yes | yes |
狭い契約に最適 | yes | no | yes |
正常なシャットダウンに対応 | yes | no | 無視する場合yes |
ログをとって継続に対応 | yes | no | 無視する場合yes |
例外による防衛的テストに対応 | yes | no | 無視する場合yes |
コンパイル時の検出と分岐のサポート | no | yes | no |
この表を見るとわかるように、noexcept
と[[throws_nothing]]
はどちらがどちらよりも優れていて対立するものではなく、それぞれ異なる目的を果たしており、Lakos Ruleを維持しながらより広い無例外指定のユースケースを満たそうとするものです。
P2947R0 Contracts must avoid disclosing sensitive information
提案中のcontract_violation
クラスのメンバ関数comment(), location()
が意味のない応答を返すようにオプトアウトできることを必須とする提案。
C++26契約プログラミングに向けて議論が進んでおり、現在の仕様では違反ハンドラ(契約違反が起きたときに呼ばれる関数)がデフォルトとなりビルドモードの概念が違反ハンドラのカスタマイズで置き換えられています。
違反ハンドラはその引数で起きた契約違反についての情報をcontract_violation
というクラスのオブジェクトとして受け取ります。contract_violation
クラスについてはP2811R7で議論中ですが、このクラスには契約違反を起こした契約条件式をテキストで取得する.comment()
と、契約違反が起きたソースコード上での場所を取得するlocation()
という2つの関数が用意されています。P2811R7では、これらの関数が意味のある応答をするのはオプショナルであり、空の文字列やstd::source_location
オブジェクトを返すことが許可されています。
これはあくまで許可でありいつも意味のある応答をする実装が前提となっていますが、この提案は意味のある応答をしないようにする(comment(), location()
が空の応答を返せるようにする)オプションを必ず提供しなければならないように規定しようとするものです。
この提案の目的はコンパイル後のバイナリファイルに不要な情報が含まれることを回避することにあります。
contract_violation
クラスのメンバ関数comment(), location()
はソースコードに関する情報を提供し、契約アノテーションは実行時に評価されるまでどれが破られるかは分かりません。すなわち、これらの関数を使用すると契約アノテーションに関するソースコードの情報がコンパイル後のバイナリに何かしらの形で書き込まれることになります。現在の契約プログラミング仕様ではビルドモードの概念が削除されているためリリースビルドにおいてもこれらの情報がバイナリに記録されることを避けることができません。これは、リバースエンジニアリングを行う人に対して大きな助けとなる情報を与えることになってしまいます。
製品のセキュリティに大きな労力を費やしているベンダーでは、この理由により契約プログラミングの使用が妨げられる可能性があります。提案では、筆者の方々の経験として次のような例が報告されています
- A社では、本番コードからログメッセージを削除することを要求された結果、コンパイル時のテキスト難読化機能とそれに付随するログテキストデコーダユーティリティを導入した
- B社では、コンパイル時に全てのログメッセージをIDに変換し、出荷バイナリと共にログメッセージをIDにマップするマップファイルを作成した。バイナリがIDのみのログを生成している間、マップファイルは社内にあり続ける(出荷されない)
- C社では、出荷バイナリにプレーンテキストでシンボル名が含まれることを避けるためにRTTIを無効化した。これはペネトレーションテストの結果を受けての措置。
これらの例は、機密性の高いテキストがバイナリに埋め込まれることを回避し知的財産を保護することを最優先事項として他の安全性やセキュリティへの配慮と同等以上のものとするために、企業が多大な労力を払い、また制限を受け入れる姿勢を示しています。
このような理由から、この提案ではcontract_violation
クラスのメンバ関数comment(), location()
が何の情報も返さないようにするオプションを提供しなければならないことを規定することで、契約プログラミングを使用するとソースコード上の情報がバイナリファイルに含まれてしまうことを回避できるようにしようとしています。
- SOC 2 Compliance: Do I need a pentest or vulnerability scanning?
- P2811R4 Contract Violation Handlers - WG21月次提案文書を眺める(2023年05月)
- P2947 進行状況
P2949R0 Slides for P2861R0: Narrow Contracts and noexcept
are Inherently Incompatable
P2861R0の紹介スライド。
P2861R0は標準ライブラリにおけるnoexcept
適用基準であるLakos Ruleとそこから得られる効用について説明したものです。このスライドはそれをWG21のメンバに紹介するものです。
P2950R0 Slides for P2836R1: std::basic_const_iterator should follow its underlying type's convertibility
P2836R1の紹介スライド。
P2836R1は、ある範囲について、そのイテレータをstd::constant_iterator
に通して得られる型とstd::ranges::cbegin()
の返すイテレータ型が異なり相互変換不可能な場合があるとして、それを正そうとするものです。詳細は上の方にあるのでそちらを参照してください。
このスライドは、LEWGのメンバにむけて提案の内容や問題点、解決方法などを解説するものです。
- P2836R0 std::const_iterator often produces an unexpected type - WG21月次提案文書を眺める(2023年05月)
- R1の内容とは少し異なっている
P2951R0 Shadowing is good for safety
↓
P2951R1 Shadowing is good for safety
変数のシャドウィングを活用した、安全性向上のための言語機能の提案。
この提案では主に安全性の向上を目的として、変数のシャドウィングの制限を解除することで安全性やコードのシンプルさに資する機能を導入しようとするもので、4つの提案が行われています。
1つ目は、void
で既存変数を再宣言することで以降そのスコープでのその変数の利用を禁止するものです。別の言い方をすると、シャドウィングを明示的に行う構文を導入しようとするものです。
現在 | 提案1 |
---|---|
#include <string> #include <vector> using namespace std; // シャドウィングのためのタグ型 struct dename{}; int main() { vector<string> vs{"1", "2", "3"}; for (auto &s : vs) { dename vs; // 以降、vsをvectorとして使用できない } } |
#include <string> #include <vector> using namespace std; int main() { vector<string> vs{"1", "2", "3"}; for (auto &s : vs) { void vs; // vsを明示的にシャドウィング // もしくは auto vs; // 以降、vsを使用できない } } |
これは例にあるようにdename
のようなクラスを標準化することによっても達成できるため、言語機能として提案しているものが受け入れられない場合はライブラリ機能でも良いとされています。ライブラリ機能ではなく言語機能である事のメリットは、エラーメッセージを改善できることにあります。
シャドウィング方法 | エラーメッセージ例 |
---|---|
void vs; |
error: 'vs' was not declared in this scope |
dename vs; |
error: 'struct dename' has no member named '*****' |
2つ目は、シャドウィング対象の変数名で変数を宣言し初期化することでシャドウィングするものです。
#include <string> #include <vector> #include <optional> using namespace std; int main() { vector<string> vs{"1", "2", "3"}; for (auto &s : vs) { // constで宣言しなおす const vector<string>& vs = vs; // 現在できない // 以降、vsはコンテナを変更しない操作のみが可能 ... } ... auto s = optional<string>{"Godzilla"}; if (s) { // optionalの中身でシャドウィング auto s = *s; // 現在できない // 以降、optionalとしてのsは必要ない ... } }
どうやらこれは、現在でもGCCでのみ意図通りに行えるようです(他のコンパイラは未初期化変数扱いになる)。
3つ目は、子スコープを導入することなく変数をシャドウィングすることを許可するものです。これは、前2つの提案と組み合わせることもできます。
#include <string> #include <vector> using namespace std; int main() { vector<string> vs{"1", "2", "3"}; // ある時点からconstにする const vector<string>& vs = vs; // 現在できない // 以降、vsはコンテナを変更しない操作のみが可能 ... }
同じスコープで同じ変数名を宣言できないため、現在でも前2つの提案でもシャドウィングするには子スコープを導入しなければなりません。この制限を解除することによって、このように最初の宣言における変数のconst
性を後から変更することができるようになります。
4つ目は、前のものとは少し毛色が異なるもので、条件付きキャストによってシャドウィングを行うものです。
#include <string> #include <optional> #include <memory> using namespace std; int main() { auto s = optional<string>{"Godzilla"}; // sをstringにキャスト(*による) if (s as string) // もしくは if (s is string) { // このスコープでは、sはstring& } else { // このスコープでは、sはoptional<string> } auto i = shared_ptr<int>{42}; if (i as int&)// もしくは if (i is int&) { // このスコープでは、iはint& } else { // このスコープでは、iはshared_ptr<int> } }
ここでのキャストは*
や.get()
等によって行われるもので、パターンマッチングにis as
を使用する提案(P2392R2)をベースとしています。
P2392ではパターンマッチングにおける利用と一般化に焦点を置いていますが、この提案ではシャドウィングとそれによってもたらされる安全性向上に焦点を当てています。
これら4つの提案は互いに直行しているため、全てを導入しなくてもどれか1つだけから導入することもできます。
この提案を導入することによって次のようなメリットが得られます
- プログラマは、イテレータや参照、ポインタの無効化の問題を回避しデバッグするために、コンパイラを使用できるようになる
- 普遍性とスレッド安全性のための
const
の活用を支援する - シャドウィングを増やすと、コードがシンプルかつ簡潔になる
また、この提案は静的解析ツールのためのヒントとなるとも述べられています。