文書の一覧
全部で44本あり、SG22(C/C++相互互換性に関する研究グループ)のCの提案を除くと36本になります。
- P0009R11 MDSPAN
- P0009R12 MDSPAN
- P0447R14 Introduction of std::colony to the standard library
- P0493R2 Atomic maximum/minimum
- P0798R6 Monadic operations for std::optional
- P1018R10 C++ Language Evolution status - pandemic edition - 2021/04
- P1068R5 Vector API for random number generation
- P1122R4 Proposed Wording for Concurrent Data Structures: Read-Copy-Update (RCU)
- P1328R1 Making std::type_info::operator== constexpr
- P1701R2 Inline Namespaces: Fragility Bites
- P2013R4 Freestanding Language: Optional ::operator new
- P2066R7 Suggested draft TS for C++ Extensions for Minimal Transactional Memory
- P2093R6 Formatted output
- P2136R3 invoke_r
- P2138R4 Rules of Design<=>Specification engagement
- P2168R3 generator: A Synchronous Coroutine Generator Compatible With Ranges
- P2280R2 Using unknown references in constant expressions
- P2291R1 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header
- P2299R1 mdspan and CTAD
- P2299R2 mdspan and CTAD
- P2314R2 Character sets and encodings
- P2325R3 Views should not be required to be default constructible
- P2328R1 join_view should join all views of ranges
- P2334R1 Add support for preprocessing directives elifdef and elifndef
- P2351R0 Mark all library static cast wrappers as [[nodiscard]]
- P2367R0 Remove misuses of list-initialization from Clause 24
- P2368R0 2020 Winter Library Evolution Polls
- P2372R0 Fixing locale handling in chrono formatters
- P2372R1 Fixing locale handling in chrono formatters
- P2374R0 views::cartesian_product
- P2374R1 views::cartesian_product
- P2375R0 Generalisation of nth_element to a range of nths
- P2376R0 Comments on Simple Statistical Functions (p1708r4): Contracts, Exceptions and Special cases
- P2377R0 [[nodiscard]] in the Standard Library: Clause 23 Iterators library
- P2380R0 reference_wrapper Associations
- P2381R0 Pattern Matching with Exception Handling
- P2382R0 Presentation Slides for P2123R0
- おわり
P0009R11 MDSPAN
↓
P0009R12 MDSPAN
多次元配列に対するstd::span
である、mdspan
の提案。
mdspan
は、multi dimensional spanの略で、連続したメモリ領域を多次元配列として参照するものです。
int* data = /* ... */ // dataをint要素4つの連続したメモリ領域として参照 auto s = std::span<int, 4>(data); // dataをint型2x2行列の連続したメモリ領域として参照 auto ms = std::mdspan<int, 2, 2>(data); // 要素アクセス ms(1, 1) = 1; ms(0, 1) = 1;
std::span
と同様に、動的な要素数を指定することもできます。
int* data = /* ... */ int size = /* ... */ auto s = std::span<int, std::dynamic_extent>(data, size); int rows = /* ... */ int cols = /* ... */ auto ms = std::mdspan<int, std::dynamic_extent, std::dynamic_extent>(data, rows, cols);
mdspan
はstd::span
よりも柔軟に設計されており、レイアウトマッピングや要素へのアクセス方法などをポリシークラスによって変更することができます。
namespace std { template < class T, class Extents, class LayoutPolicy = std::layout_right, class Accessor = std::accessor_basic > class basic_mdspan; template <class T, ptrdiff_t... Extents> using mdspan = basic_mdspan<T, std::extents<Extents...>>; }
LayoutPolicy
は多次元インデックス(整数値の列i0, i1, ..., in
)をメモリ上の一点を指す単一のインデックス(整数値i
)に変換するもので、Accessor
はLayoutPolicy
によって得られたインデックスとメモリを指すポインタを要素1つの参照へ変換するものです。
template < class T, class Extents, class LayoutPolicy = std::layout_right, class Accessor = std::accessor_basic > class basic_mdspan { public: using extents_type = Extents; using layout_type = LayoutPolicy; using accessor_type = AccessorPolicy; using mapping_type = typename layout_type::template mapping_type<extents_type>; private: accessor_type acc_{}; mapping_type map_{}; pointer ptr_{}; public: template<class... SizeTypes> constexpr reference operator()(SizeTypes... indices) const noexcept { // LayoutPolicyによってインデックス列を連続領域への単一インデックスに変換し // Accessorによって、インデックスとポインタから要素を引き当てる return acc_.access(ptr_, map_(indices...)); } }
例えばdouble
の2次元配列ならば、LayoutPolicy
はインデックスx, y
と配列の幅w
を用いて、i = y * w + x
を返し、Accessor
はそれと領域へのポインタptr
用いてptr[i]
を返すものになります。
また、submdspan()
という関数によって、mdspan
からsliceを取得することができます。
namespace std { // [mdspan.submdspan], submdspan creation template<class ElementType, class Extents, class LayoutPolicy, class AccessorPolicy, class... SliceSpecifiers> constexpr basic_mdspan<see below> submdspan(const basic_mdspan<ElementType, Extents, LayoutPolicy, AccessorPolicy>& src, SliceSpecifiers... slices) noexcept; }
提案文書より、使用例
// メモリ領域へのマッピングを作成(LayoutPolicyの作成) using Extents3D = extents<3, dynamic_extent, 7>; layout_right::template mapping<Extents3D> map_right(10); // メモリ領域確保 int* ptr = new int[3 * 8 * 10]; // mdspanの構築(3x10x7の三次元行列として参照) basic_mdspan<int,Extents3D,layout_right> a(ptr, map_right); // mdspnによる領域の初期化 for(int i0 = 0; i0 < a.extent(0); i0++) // i0 = 0 -> 2 for(int i1 = 0; i1 < a.extent(1); i1++) // i1 = 0 -> 9 for(int i2 = 0; i2 < a.extent(2); i2++) // i2 = 0 -> 7 a(i0, i1, i2) = 10000 * i0 + 100 * i1 + i2; // subspanの取得(あるいは、sliceの取得) // [1, [4...5], [1...5]]の範囲を参照するmdspanを得る auto a_sub = submdspan(a, 1, pair<int, int>(4, 6), pair<int, int>(1, 6)); // subspanの内容を出力 for(int i0 = 0; i0 < a_sub.extent(0); i0++) { for(int i1 = 0; i1 < a_sub.extent(1); i1++) { cout << a_sub(i0, i1) << " "; } cout << endl; } /* Output 10401 10402 10403 10404 10405 10501 10502 10503 10504 10505 */
この提案は線形代数ライブラリ整備の一環として、Liblary Fundamentals v3に向けて議論されています。現在はLWGにて検討中です。
- std::span - cpprefjp
- A Gentle Introduction to mdspan - kokkos/mdspan
- kokkos/mdspan - Github
- P0009 進行状況
P0447R14 Introduction of std::colony to the standard library
要素が削除されない限りそのメモリ位置が安定なコンテナであるstd::colony
の提案。
以前の記事を参照
- P0447R11 Introduction of std::colony to the standard library - [C++]WG21月次提案文書を眺める(2020年12月)
- P0447R12 Introduction of std::colony to the standard library - [C++]WG21月次提案文書を眺める(2021年01月)
- P0447R13 Introduction of std::colony to the standard library - [C++]WG21月次提案文書を眺める(2021年04月)
このリビジョンでの変更は、get_iterator_from_pointer()
の名前を変更するなど調整したことなどです。
P0493R2 Atomic maximum/minimum
std::atomic
に対して、指定した値と現在の値の大小関係によって値を書き換えるmaximum/minimum操作であるfetch_max()/fetch_min()
を追加する提案。
以前の記事を参照
このリビジョンでの変更は、各関数の処理前後に値が変わらないような呼び出しをされた場合に、出力操作が行われるかを未規定にしたことです。
P0798R6 Monadic operations for std::optional
std::optional
にmonadic interfaceのサポートを追加する提案。
std::optional
はvocabulary typeとして、C++プログラミングの色々なところで活用することができ、活用されています。しかし、optional
が無効値を取る可能性のある計算を連鎖させた場合、その有効性をチェックするif
文が必要となるため、可読性を著しく低下させてしまいます。
// 画像に移っている猫を可愛くする関数 image get_cute_cat (const image& img) { return add_rainbow( make_smaller( make_eyes_sparkle( add_bow_tie( crop_to_cat(img)))); }
そもそも画像に猫が写っていないとき、目を閉じているとき、ネクタイを付けるいい場所がない時、などそれぞれの処理は失敗する可能性があります。そこで、optional
が利用できるわけですが、現在のC++のstd::optional
だとif
が連なる次のようなコードになります。
std::optional<image> get_cute_cat (const image& img) { auto cropped = crop_to_cat(img); if (!cropped) { return std::nullopt; } auto with_tie = add_bow_tie(*cropped); if (!with_tie) { return std::nullopt; } auto with_sparkles = make_eyes_sparkle(*with_tie); if (!with_sparkles) { return std::nullopt; } return add_rainbow(make_smaller(*with_sparkles)); }
このようなコードでは、ほぼ同じだけど細部が異なるボイラープレートなコードが増産され、可読性の低下を招き、記述ミスをしやすくなってしまっています。
この提案は、monadic interfaceをstd::optional
に追加することによって、このような処理のチェーンをより簡易に書けるようにするものです。先程までの例は次のように書き直すことができます
std::optional<image> get_cute_cat (const image& img) { return crop_to_cat(img) .and_then(add_bow_tie) .and_then(make_eyes_sparkle) .transform(make_smaller) .transform(add_rainbow); }
先程まで現れていた有効性チェックのif
文はand_then
やtransform
の中に隠蔽され、それらはstd::optional
を返すことによってメソッドチェーンによる処理の連鎖が表現できるようになっています。
追加を提案しているのは次の三種類の処理で、すべてメンバ関数です
.transform()
.and_then()
.or_else()
.transform()
はstd::optional<T>
をstd::optional<U>
へ変換するもので、有効値を保持している場合にのみ渡された関数を適用して有効値の変換を行います。
.and_then()
も.transform()
と同じく有効値を保持している場合にのみ渡された関数を実行してstd::optional<T>
をstd::optional<U>
へ変換するものですが、こちらは渡す関数がstd::optional<U>
を直接返すことができます。それによって、有効値 → 無効値の変換が可能になります。
.or_else()
は.and_then()
と双対的な関係にあるもので、こちらはstd::optional<T>
が無効値を保持してる場合にのみ渡された関数を実行するものです。無効値 → 有効値の変換が可能になります。
// 文字列を int に変換し、失敗した場合 std::nullopt を返すような関数 std::optional<int> StoI(string s); int main() { std::optional<string> opts = "abc"; // opts が値を持つ場合、std::optional<size_t>{ opts->size() } を返す。 // 持たない場合、std::optional<size_t>{ std::nullopt } を返す。 auto s = opts.transform([](auto&& s){ return s.size(); }) // opts が値を持つ場合、StoI(*opts) を返す。 // 持たない場合、std::optional<int>{ std::nullopt } を返す。 // その後、結果が有効値を保持している場合にのみ出力する opts .and_then(StoI) .and_then([](int n) { std::cout << n << '\n'; return n; }); // opts が値を持つ場合、自身を返す。 // 持たない場合、与えられた関数を実行して // std::optional<string>{ std::nullopt } を返す。 // その後、プログラムを終了させる opts .or_else([]{ cout << "failed.\n"; return std::nullopt; }) .or_else([]{ std::terminate(); }); }
宣伝
この部分では「ゲーム開発者のための C++11〜C++20 技術書典 10 Ver.」という本から文章やサンプルコードを引用・改変しています。気になった方はぜひお買い求めください。
宣伝2
この記事(not 提案)を書いている人が、同じ目的でより広い対象により多様なmonadic interfaceを提供するC++20のライブラリを書いています。良かったら使ってみてください。
P1018R10 C++ Language Evolution status - pandemic edition - 2021/04
EWG(コア言語への新機能追加についての作業部会)が2021/04に議論した提案やIssueのリストや将来の計画、テレカンファレンスの状況などをまとめた文書。
- P0847R6 Deducing
this
- P0849R7 auto(x): DECAY_COPY in the language
- P2242R2 Non-literal variables (and labels and gotos) in constexpr functions
これらの提案はC++23入りを目指してCWGに転送されました。
P1068R5 Vector API for random number generation
<random>
にある既存の分布生成器にイテレータ範囲を乱数で初期化するAPIを追加する提案。
以前の記事を参照
このリビジョンでの変更は、
- 提案するベクターAPIを
operator()
からgenerate()
という名前の関数に変更 - コンセプトを使用した制約を行うようにした
range
オブジェクトを受けてその範囲を乱数で初期化するオーバーロードを追加した- 以前の
uniform_vector_random_bit_generator
を削除し、uniform_bulk_random_bit_generator/uniform_range_random_bit_generator
の二つのコンセプトを追加した - パフォーマンスについて議論を追加した
などです。
P1122R4 Proposed Wording for Concurrent Data Structures: Read-Copy-Update (RCU)
標準ライブラリにRead-Copy-Update(RCU)を導入する提案。
以前の記事を参照
このリビジョンでの変更は、LWGのレビューを受けて文言を調整したことなどです。
この提案は2021年6月の全体会議で投票にかけられ、Concurrency TS v2に導入されることが決まりました。標準ライブラリにいつ入るのかは未定です。
P1328R1 Making std::type_info::operator== constexpr
std::type_info
の==
をconstexpr
にする提案。
この提案に先んじて、C++20ではtypeid
の定数式での実行が許可されていましたが、そのメンバ関数はどれもconstexpr
ではなかったためにstd::type_info
オブジェクトを定数式で使用することはできませんでした。
この提案は、operator==
にconstexpr
を付加しておくことでstd::type_info
オブジェクトを定数式で使用できるようにするものです。
template<typename T, typename U> constexpr bool type_eq(const T* t, const U* u) { return typeid(t) == typeid(u); // 現在はここでエラー } int main () { constexpr int n = 0; constexpr long l = 0; constexpr bool b = type_eq(&n, &l); }
この提案は当初C++20入りを目指していたのですが、LWGの議論中に実際に実装可能かどうかについて疑問が呈され、その確認に時間がかかったためC++20に間に合いませんでした。
現在はその疑問は解消されているようで、次の全体会議にてC++23入りの投票にかけられることが決まっています。
P1701R2 Inline Namespaces: Fragility Bites
inline
名前空間の名前探索に関するバグを修正する提案。
以前の記事を参照
このリビジョンでの変更は、EWGでの議論で浮かび上がった選択肢について表にまとめ、標準へ提案する文言を追加した事です。
P2013R4 Freestanding Language: Optional ::operator new
フリースタンディング処理系においては、オーバーロード可能なグローバル::operator new
を必須ではなくオプションにしようという提案。
以前の記事を参照
- P2013R1 : Freestanding Language: Optional ::operator new - [C++]WG21月次提案文書を眺める(2021年04月)
- P2013R3 : Freestanding Language: Optional ::operator new - [C++]WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は、フリースタンディング処理系におけるグローバルなnew/delete
の定義が何をするかは実装定義となるようにされたことなどです。
この提案は現在CWGのレビューを終え、ライブラリの部分についてLWGのレビュー待ちをしています。それが終わったら全体投票にかけられる予定です。
P2066R7 Suggested draft TS for C++ Extensions for Minimal Transactional Memory
現在のトランザクショナルメモリTS仕様の一部だけを、軽量トランザクショナルメモリとしてC++へ導入する提案。
以前の記事を参照
- P2066R2 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年05月)
- P2066R3 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年09月)
- P2066R4 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年10月)
- P2066R5 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年02月)
- P2066R6 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年03月)
このリビジョンの変更点は、標準ライブラリの機能で同期の問題が発生しうるものをさらに除外した事です。例えば、<chrono>
の時計型やロケール関連のものです。
この提案はLEWGからCWGへ転送されようとしているところで、とりあえずTransactional Memory TS v2を目指すようです。
P2093R6 Formatted output
std::format
によるフォーマットを使用しながら出力できる新I/Oライブラリstd::print
の提案。
前回の記事を参照
- P2093R0 Formatted output - [C++]WG21月次提案文書を眺める(2020年6月)
- P2093R1 Formatted output - [C++]WG21月次提案文書を眺める(2020年7月)
- P2093R2 Formatted output - [C++]WG21月次提案文書を眺める(2020年10月)
- P2093R3 Formatted output - [C++]WG21月次提案文書を眺める(2021年1月)
- P2093R4 Formatted output - [C++]WG21月次提案文書を眺める(2021年2月)
- P2093R5 Formatted output - [C++]WG21月次提案文書を眺める(2021年3月)
このリビジョンでの変更は、配置されるヘッダが<io>
から<print>
に変更されたこと、ユニコード出力時に無効なエンコーディングを置換するU+FFFDの選択を明確にしたこと、ユニコード出力時にリテラルエンコーディング(実行時エンコーディング)を使用することを明確にしたこと、文字集合を指定するためのANSIエスケープコードはこの提案を実装するためのネイティブシステムAPIとはみなされない事を明確にした、ことなどです。
P2136R3 invoke_r
戻り値型を指定するstd::invoke
であるinvoke_r
の提案。
以前の記事を参照
このリビジョンでの変更は機能テストマクロを追加した事です。
この提案は2021年6月の全体会議で承認され、C++23入りが決定しました。
P2138R4 Rules of Design<=>Specification engagement
CWGとEWGの間で使用されているwording reviewに関するルールの修正と、それをLWGとLEWGの間でも使用するようにする提案。
以前の記事を参照
- P2138R1 : Rules of Design <=> Specification engagement - [C++]WG21月次提案文書を眺める(2020年4月)
- P2138R2 : Rules of Design <=> Specification engagement - [C++]WG21月次提案文書を眺める(2020年6月)
- P2138R3 : Rules of Design <=> Specification engagement - [C++]WG21月次提案文書を眺める(2020年9月)
このリビジョンでの変更は、Tentatively Readyという言葉を明確にしたことなどです。
P2168R3 generator: A Synchronous Coroutine Generator Compatible With Ranges
Rangeライブラリと連携可能なT
型の要素列を生成するコルーチンジェネレータstd::generator<T>
の提案。
前回の記事を参照
- P2168R0 generator: A Synchronous Coroutine Generator Compatible With Ranges - WG21月次提案文書を眺める(2020年5月)
- P2168R1 generator: A Synchronous Coroutine Generator Compatible With Ranges - WG21月次提案文書を眺める(2021年01月)
- P2168R2 generator: A Synchronous Coroutine Generator Compatible With Ranges - WG21月次提案文書を眺める(2021年04月)
このリビジョンでの変更はよくわかりません。
P2280R2 Using unknown references in constant expressions
定数式での参照のコピーを許可する提案。
以前の記事を参照
このリビジョンでの変更は、参照と同じことをポインタにも拡張したことです。
template <typename T, size_t N> constexpr size_t array_size(T (&)[N]) { return N; } template <typename T, size_t N> constexpr size_t array_size_p(T (*)[N]) { return N; } void check(int const (¶m)[3]) { constexpr auto s1 = array_size(param); // ok、R0 constexpr auto s2 = array_size_p(param); // ok、R2 }
P2291R1 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header
std::to_chars, std::from_chars
を整数変換に関してconstexpr
にする提案。
以前の記事を参照
このリビジョンでの変更は、機能テストマクロ__cpp_lib_constexpr_charconv
を追加した事です。
P2299R1 mdspan
and CTAD
↓
P2299R2 mdspan
and CTAD
提案中のstd::mdspan
のCTAD対応についての問題を報告する文書。
以前の記事を参照
このリビジョンでの変更は、推論補助を追加することでこの問題に対処することにしたことです。
以前の検討では、推論補助の追加によってこの問題に対処することはできなかったのですが、どうやらそれはエイリアステンプレートでの実引数推論を実装していたGCC/MSVCのバグであったようです。
namespace std { template<class ElementType, class Extents, class LayoutPolicy = layout_right, class AccessorPolicy = default_accessor<ElementType>> class basic_mdspan; // この推論補助を追加する template <class ElementType, class... IndexTypes> explicit basic_mdspan(ElementType*, IndexTypes...) -> basic_mdspan<ElementType, extents<[] (auto) constexpr { return dynamic_extent; } (identity<IndexTypes>{})...>>; template<class T, size_t... Extents> using mdspan = basic_mdspan<T, extents<Extents...>>; } int main() { // 何かメモリ領域 double* data = ...; // 動的サイズのmdspan構築、この提案の後ではOK mdspan a2(data, 64, 64); }
P2314R2 Character sets and encodings
規格文書中の ~ character setという言葉を明確に定義し直す提案。
以前の記事を参照
- P2314R0 Character sets and encodings - [C++]WG21月次提案文書を眺める(2021年02月)
- P2314R1 Character sets and encodings - [C++]WG21月次提案文書を眺める(2021年03月)
このリビジョンでの変更は、r-char-sequence(生文字列リテラルの文字列)にユニバーサル文字名が含まれていない事を明確にした事と、文字列リテラルオブジェクトが翻訳フェーズ5で初期化済みと取れる文章の削除、提案する文言の修正などです。
P2325R3 Views should not be required to be default constructible
Viewとみなされる型にデフォルト構築可能性を要求しない様にする提案。
以前の記事を参照
- P2325R0 Views should not be required to be default constructible - [C++]WG21月次提案文書を眺める(2021年02月)
- P2325R1 Views should not be required to be default constructible - [C++]WG21月次提案文書を眺める(2021年03月)
- P2325R2 Views should not be required to be default constructible - [C++]WG21月次提案文書を眺める(2021年04月)
このリビジョンでの変更は、標準に提案する文言を編集した事です。
この提案は2021年6月の本会議で投票にかけられ、C++20に向けて採択されました。歴史修正です。
P2328R1 join_view should join all views of ranges
std::ranges::join_view
の制約を緩和して、prvalueのview
ではないrange
を平坦化できるようにする提案。
以前の記事を参照
このリビジョンでの変更は、LWGのレビューのフィードバックを受けて、constexpr
が欠けていたところに追加したことなどを修正したことです。
この提案は2021年6月の本会議で投票にかけられ、C++20に向けて採択されました。歴史修正その2です。
P2334R1 Add support for preprocessing directives elifdef and elifndef
#elif
でマクロの定義の有無で条件分岐する糖衣構文となるプリプロセッシングディレクティブである#elifdef/#elifndef
の提案。
このリビジョンでの変更はよくわかりません。
この提案は、CWGに転送するためにEWGでの投票待ちをしています。C++23入りを目指しているようです。
P2351R0 Mark all library static cast wrappers as [[nodiscard]]
static_cast
のラッパであるような標準ライブラリの関数に[[nodiscard]]
を付ける提案。
std::move
を始めとする標準ライブラリの型キャストラッパな関数は、コンパイラに対して引数が消費される関数として動作しているため、その戻り値を無視しても警告されません。一方、同等のことをstatic_cast
を始めとる言語機能のキャストによって行うと、警告が発せられます。
void f(auto T val) { // -Wallだと警告が発せられる val; static_cast<T&&>(val); // -Wallでも警告されない std::move(val); }
これらの型キャスラッパな関数はその結果を使用しないと意味がないため、結果が捨てられている場合はほぼバグです。これを検出するために、それらの関数に[[nodiscard]]
を付加する事でコンパイル時に警告として発せられるようにしようとする提案です。
提案されている候補は以下のものです。
to_integer
forward
move
move_if_noexcept
as_const
to_underlying
identity
bit_cast
MSVCではすでにこれらの関数に[[nodiscard]]
が付加されており、筆者の方の所属企業(avast)の社内のコードベースにおいていくつかのバグを発見するのに役立っており、他の社員の人からもこのアプローチは好評のようです。
P2367R0 Remove misuses of list-initialization from Clause 24
<ranges>
の規定において、誤ったリスト初期化({}
)の使用を修正する提案。
リスト初期化では、その初期化式の評価順序が決まっており、縮小変換が禁止されています。
<ranges>
のRange AdopterやCPOなどは、expression-equivalentという言葉によってある式が別の式と同じ効果を持つように規定されています。その効果では、リスト初期化が使用されていたり、カンマによる式が使用されていたりしますが、それら評価順が規定されている式を使用しているために、実装不可能な規定となっている箇所があります。
例えばdrop_view
を見てみると
The name
views::drop
denotes a range adaptor object. LetE
andF
be expressions, letT
beremove_cvref_t<decltype((E))>
, and letD
berange_difference_t<decltype((E))>
(中略) the expressionviews::drop(E, F)
is expression-equivalent to:
- IfT
is a specialization ofranges::empty_view
, then((void) F, decay-copy(E))
.
- (中略)
- Otherwise,ranges::drop_view{E, F}
.
この効果の1つ目は((void) F, decay-copy(E))
で、2つ目はranges::drop_view{E, F}
ですが、前者はF -> E
の評価順が規定されている一方で、後者はE -> F
の順の評価順が規定されています。これは矛盾しており、実際にはviews::drop(E, F)
のように呼び出されてからこれらの効果が選択されるため、効果の中で式の評価順をコントロールすることは実質的に不可能です。
また、リスト初期化においては定数式でその値が受け止められるときにのみ縮小変換を許可するという性質から、CPOは呼び出された定数値を伝播しチェックする必要があります。
template<typename T> void f(T&& E) { // Tのdifference_typeはint32_tとすると // これはOKとする views::drop(E, int64_t()); int64_t l = 0; // こっちはエラーとする views::drop(E, l); }
それによって、このようなハンドリングを要求している事になりますが、どうやらこれは意図されたものではないようです。
この提案では、これらの問題を解消するために{}
を用いているところを()
に置き換えています。ただしその範囲は限定的であり、明らかにこれらの問題を来している箇所だけに留められています。
さらに、この提案を書いてる最中に発見された、views::single
に関する次の2つの問題についても同時に解決しています。
- 暗黙の推論補助はCV修飾されたrvalueを
remove_cv
したり、配列型や関数型をdecay
する事ができない。これはRange-v3の仕様と矛盾している views::single(a_single_view)
はラップではなくコピーされる
この提案はIssue解決であることもあり、LWGでのレビューが素早く終了していました。そして、2021年6月の全体会議でC++20に向けて採択されました。
- LWG Issue 3524. Unimplementable narrowing and evaluation order requirements for range adaptors
- P2367 進行状況
P2368R0 2020 Winter Library Evolution Polls
2021年春にLEWGにおける最終投票にかけられる(た)提案のリスト。
- P0323R10 expected
- P2325R2 Views Should Not Be Required To Be Default Constructible
- P2328R0 join_view Should Join All Views Of Ranges
- P2210R2 Superior String Splitting
- P2321R1 views::zip
- P2251R1 Require span & basic_string_view To Be Trivially Copyable
- P1072R7 basic_string::resize_and_overwrite
- P2340R0 Clarifying The Status Of The "C Headers"
- P2301R0 Add A pmr Alias For stacktrace
これらはLEWGでの議論が完了したためLWGへ送付する確認を取るための投票にかけられます。ここで異論がなければ、LWGにおいて最後のチェックが行われ、ドラフトに反映されます。
このうちP2325R2、P2328R0、P2210R2の3つはC++20へ逆適用しようとしています(その後この3つは2021年6月の全体会議でC++20に向けて承認されました)。
P2372R0 Fixing locale handling in chrono formatters
↓
P2372R1 Fixing locale handling in chrono formatters
<chrono>
のフォーマッタがロケール依存でありそれを制御できない問題を修正する提案。
C++20にて<chrono>
にカレンダーとタイムゾーンの拡張が入り、<format>
の追加とともにそれらに対してのフォーマッタが提供されました。これはそれぞれ別々の提案によって行われていたため、ローケルに関して設計上の問題が見落とされたままになってしまたようです。
// ロシア語のロケールをグローバルロケールに設定 std::locale::global(std::locale("ru_RU")); std::string s1 = std::format("{}", 4.2); // s1 == "4.2" (ローカライズされない) std::string s2 = std::format("{:L}", 4.2); // s2 == "4,2" (ローカライズされる) using sec = std::chrono::duration<double>; std::string s3 = std::format("{:%S}", sec(4.2)); // s3 == "04,200" (ローカライズされる)
このように、std::format
の設計と一貫しなくなってしまっています。この場合にロケール非依存にするためにはフォーマットを手動で行う必要があります。
また、%S %M
などのchrono
のフォーマット指定子はO
を付けて%OS %OM
とする事でロケール依存の表現を出力するように説明されているため、あたかもロケール非依存であるかのように思わせてくれますが、実際にはこの二つは同じ効果でどちらもロケール依存の表現を出力します。
この提案は、これらの問題を解決するために、chrono
のフォーマッタをデフォルトでロケール非依存にし、他の全ての標準フォーマッタがそうであるようにL
指定子でロケール依存フォーマットを明示的にオプトインするように変更するものです。
Before | After |
---|---|
auto s1 = std::format("{:%S}", sec(4.2)); // s1 == "04,200" auto s2 = std::format("{:L%S}", sec(4.2)); // throws format_error |
auto s1 = std::format("{:%S}", sec(4.2)); // s1 == "04.200" auto s2 = std::format("{:L%S}", sec(4.2)); // s2 == "04,200" |
この提案はSG16, LEWGの議論と投票においてC++23に導入された場合にC++20へのDRとして適用される事に合意が取れています。まだLEWGでの議論の最中ですが、筆者の方がやる気なのでそうなりそうです。
std::format
- cpprefjp- LWG Issue 3547. Time formatters should not be locale sensitive by default
- P2372 進行状況
P2374R0 views::cartesian_product
↓
P2374R1 views::cartesian_product
任意個数のシーケンスの直積を取って、その元のシーケンスを生成するcartesian_product_view
の提案。
これが何をするものなのかは、サンプルコードを見ると一目瞭然でしょう。
Before | After |
---|---|
std::vector<int> a,b,c; for (auto&& ea : a) { for (auto&& eb : b) { for (auto&& ec : c) { use(ea,eb,ec); } } } |
std::vector<int> a,b,c; for (auto&& [ea,eb,ec] : std::views::cartesian_product(a,b,c)) { use(ea,eb,ec); } |
このような異なるシーケンスを総当たりする多重ループを一つのループに纏めることができます。
また、次のようにすると単なる多重ループをひとまとめにできます(使いやすいかはともかく・・・)
constexpr int N = ...; constexpr int M = ...; constexpr int L = ...; for (auto [i, j, k] : std::views::cartesian_product(std::views::iota(0, N), std::views::iota(0, M), std::views::iota(0, L))) { // ... }
これは入力となるシーケンス1つを1つの集合とみなし、それらの間での直積集合を作ってその元(順序対)をイテレートすることに対応しており、直積のことをデカルト積(cartesian product)と呼ぶためこの名前になっています。
cartesian_product_view
の入力となるrange
はforward_range
でなくてはならず、reference
はpair/tuple
のいずれかになり、その要素型はベースのrange
のイテレータのreference
となります。すなわち、ベースのrange
のイテレータが参照を返す場合はその要素はコピーされません。
また、cartesian_product_view
はパイプライン演算子による入力をサポートしていません。なぜなら、a | views::cartesian_product(b, c)
とviews::cartesian_product(b, c)
の使用を区別できないためです。
P2375R0 Generalisation of nth_element to a range of nths
std::nth_element
を一般化したnth_elements
の提案。
std::nth_element
は入力の範囲について[first, ..., nth, ..., last)
の関係にある3つのイテレータを受け取って、この範囲を[first, nth)
、[nth]
、(nth, last)
の3つの範囲を連結したものになるように並べ替えます。
この時、[first, nth)
の最大要素をmax
、(nth, last)
の最小要素をmin
とするとmax < *nth <= min
(デフォルトの比較の場合)とはなりますが、範囲全体は必ずしもソートされません。また、[first, nth)
と(nth, last)
内の順序は未規定です。
そして、nth
の位置にある要素は、[first, last)
をソートした場合にnth
の位置に来る要素に一致します。
nth_elements
はそれを一般化し、n番目を指すイテレータnth
を取る代わりに[first, ..., nth_first, ..., nth_last, ..., last)
の関係にある4つのイテレータを受け取って、この範囲を[first, nth_first)
、[nth_first, nth_last)
、(nth_last, last)
の3つの範囲を連結したものになるように並べ替えます。この時、(first, nth_first)
の最大要素をmax
、[nth_last, last)
の最小要素をmin
とするとmax < *nth_first < *nth_last <= min
(デフォルトの比較の場合)となります。
そして、[nth_first, nth_last)
の範囲はソートされており、、[first, last)
全体をソートした場合に[nth_first, nth_last)
の範囲に来る要素に一致します。それ以外のことはnth_element
と同様になります。
cpprefjpのサンプルコードを少し改変した例
int main() { std::vector<int> v1 = {5, 10, 4, 7, 1, 9, 8, 6, 2}; // 4番目に小さい値より小さい値を前に集める std::nth_element(v1.begin(), v1.begin() + 3, v1.end()); for (auto x : v1) { std::cout << x << ", "; }; // 2, 1, 4, 5, 7, 6, 8, 9, 10, std::cout << '\n'; std::vector<int> v2 = {5, 10, 4, 7, 1, 9, 8, 6, 2}; // 3~6番目に小さい値より小さい値を前に集める std::ranges::nth_elements(v2, std::ranges::subrange{v2.begin() + 2, v2.begin() + 5}); for (auto x : v2) { std::cout << x << ", "; }; // 2, 1, 4, 5, 6, 7, 8, 9, 10, }
[first, last)
の長さをN
、[nth_first, nth_last)
の範囲にあるユニークな要素の数をm
とすると、この操作はO(N log m)
の計算量となるようです。
要するに入力範囲を部分的にソートするものですが、std::partial_sort
と異なるのは開始位置が制限されていないことで、範囲の任意の部分範囲についてソートすることができます。
このアルゴリズムはNumPyにおいてnumpy.partition
として実装されているなど、使用と分析は広く成熟しているため、C++でも利用可能とするために提案されています。
P2376R0 Comments on Simple Statistical Functions (p1708r4): Contracts, Exceptions and Special cases
P1708R4で提案されている統計関数について、例外を投げないようにする提案。
P1708R4では、必要な条件が満たされない場合にstats_error
という例外を投げるようになっています。この提案は、既存の関数(std::log, std::sqrt
など)の振る舞いや他の言語のライブラリなどを参考にして、例外を投げないようにしようとするものです。
P2377R0 [[nodiscard]] in the Standard Library: Clause 23 Iterators library
<iterator>
にあるものの一部に[[nodiscard]]
を付加する提案。
対象は多岐にわたるため転記しませんが、特筆するところとしては、標準ライブラリにある全てのCPOに対して戻り値型がvoid
ではない場合にその関数呼び出し演算子に[[nodiscard]]
が付加することを提案しています。
P2380R0 reference_wrapper Associations
Networking TSで用意されている、associated_allocator
とassociated_executor
に対して、reference_wrapper<T>
の特殊化を追加する提案。
この2つはまとめてassociatorと呼ばれ、名前付き要件ProtoAllocatorとExecutorを満たすようなアロケータとエグゼキュータを、非同期操作に指定された完了ハンドラから取得します。
Networking TSの非同期モデルでは、associatorによって非同期処理で使用するアロケータとエグゼキュータを取得します。
// 何かの非同期処理 template<typename Handler> void async_f(handler h) { // ハンドラに関連づけられたアロケータの取得 auto alloc = std::net::associated_allocator<Handler>::get(h); // ハンドラに関連づけられたエグゼキュータの取得 auto ex = std::net::associated_executor<Handler>::get(h); }
reference_wrapper<T>
はクラスオブジェクトの参照を転送するために使用でき、T
のCallable(関数呼び出し可能)性を受け継ぎ、関数呼び出しを内部参照にバイパスします。別の言い方をすると、Callableオブジェクトに参照のセマンティクスを与える事ができます。
<algorithm>
のアルゴリズム関数などもそうですが、標準ライブラリのものはCallableオブジェクトを取る時に値として受け取ります。Networking TSでも同様で、例えばコピーが重いハンドラを渡そうとするときにreference_wrapper<T>
を使用したくなるのですが、reference_wrapper<T>
にはassociatorのサポートが無いためそのままだとT
に関連づけられたアロケータ/エグゼキュータを使ってもらう事ができません。
このような場合にreference_wrapper<T>
が使えなければ良いのですが、associatorは特殊化が無い場合にデフォルトのアロケータ/エグゼキュータを使うので一見すると問題なく使えているように見えてしまいます。その際、T
に対するassociatorがユーザによって特殊化されている場合、気づかない間に間違ったコードを書いてしまう事になりかねません。
// オリジナルのハンドラ struct my_handler { // ... }; // associatorの特殊化 (1) std::net::associated_allocator<my_handler> { // ... }; std::net::associated_executor<my_handler> { // ... }; int main() { my_handler h{}; // カスタマイズされたassociator (1)を使用する async_f(h); // OK // デフォルトのassociatorを使用する async_f(std::ref(h)); // OK }
reference_wrapper
はCallableオブジェクトを参照として転送する際の標準的なソリューションであり、このような使用は自然な用法であるため、associatorのサポートをデフォルトで用意しようという提案です。
- Networking TS の Boost.Asio からの変更点 - その 4: Associated Executor - あめだまふぁくとりー
std::reference_wrapper
- cpprefjp- P2380 進行状況
P2381R0 Pattern Matching with Exception Handling
パターンマッチングにおいて、例外のキャッチのためのパターンを追加する提案。
現在のパターンマッチングP1371R3では、いくつかのエラーハンドリング方法が用意されていますが、例外に対するパターンは提供されていません。
// expectef<T, E>のようにエラーと正常値をまとめて返す variant<rete, error_code> e() noexcept; variant<reto, error_object> o() noexcept; inspect(e()) { <rete> //... <error_code> //... } inspect(o()) { <reto> //... <error_object> //... } // 例外を投げうる reta a();// throws b, c, d // 例外をキャッチするにはinspect式をtry-catchで囲う try { inspect(a()) { <reta> //... } } catch(b) { } catch(c) { } catch(d) { }
パターンマッチングは例外ベースのエラー処理をサポートしておらず、戻り値ベースの例外処理を優遇しています。標準ライブラリのほとんどのものは例外ベースのエラー処理機構を採用しており、このことは標準の仕様と一貫していません。
この提案は、パターンマッチングにおいて例外パターンを許可する事で、例外ベースのエラー処理を簡潔に書く事ができるようにするものです。この提案の後では、先ほどのコードは次のように書く事ができるようになります。
// 例外を投げうる reta a();// throws b, c, d inspect(a()) { <reta> //... <b> //... <c> //... <d> //... }
エラー処理はプログラミング全般にとって基本的であるためこのような小さな改善が大きな利益をもたらす可能性があります。また、C++におけるエラー処理方法と消費方法の多様性は多くの問題を引き起こしていますが、多くの提案や議論では消費の方法よりもパフォーマンスやエラーの生成について焦点を当てています。この提案は消費に焦点を当て、多様なエラー処理機構に対するその消費処理をなるべく統一的に扱えるようにするものです。
P2382R0 Presentation Slides for P2123R0
P2123R0の解説するためのスライド。
おそらくLEWGのメンバに向けて書かれたものです。
P2123R0は、ABI安定性の向上のために型システムを拡張しよう!という提案で、interface(tag){}
のようなブロックでABIバージョンごとに処理を分け、それを使用する時にinterface(tag)
を付加することでしようするバージョンを決定できるようにしようとするものです。