文書の一覧
全部で74本あり、SG22(C/C++相互互換性に関する研究グループ)のCの提案を除くと73本になります。
- 採択された文書
- P0798R8 Monadic operations for std::optional
- P1147R1 Printing volatile Pointers
- P1272R4 Byteswapping for fun&&nuf
- P2077R3 Heterogeneous erasure overloads for associative containers
- P2314R4 Character sets and encodings
- P2415R2 What is a view?
- P2418R2 Add support for std::generator-like types to std::format
- P2432R1 Fix istream_view, Rev 1
- P2450R0 C++ Standard Library Issues to be moved in Virtual Plenary, Oct. 2021
- P2462R0 Core Language Working Group “ready” issues for the October, 2021 meeting
- その他の文書
- N4896 PL22.16/WG21 agenda: 4 October 2021, Virtual Meeting
- N4897 WG21 admin telecon meeting: September 2021
- N4898 WG21 2021-10 Virtual Meeting Minutes of Meeting
- N4899 WG21 admin telecon meetings: 2022
- N4900 WG21 virtual plenary meeting(s): 2022
- N4901 Working Draft, Standard for Programming Language C++
- N4902 Editors' Report - Programming Languages - C++
- P0009R13 MDSPAN
- P0627R6 Function to mark unreachable code
- P1169R3 static operator()
- P1467R5 Extended floating-point types and standard names
- P1642R7 Freestanding Library: Easy [utilities], [ranges], and [iterators]
- P1673R5 A free function linear algebra interface based on the BLAS
- P1854R1 Conversion to literal encoding should not lead to loss of meaning
- P1885R8 Naming Text Encodings to Demystify Them
- P2012R2 Fix the range-based for loop, Rev2
- P2066R10 Suggested draft TS for C++ Extensions for Minimal Transactional Memory
- P2248R2 Enabling list-initialization for algorithms
- P2249R2 Mixed comparisons for smart pointers
- P2255R2 A type trait to detect reference binding to temporary
- P2264R1 Make assert() macro user friendly for C and C++
- P2264R2 Make assert() macro user friendly for C and C++
- P2291R3 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header
- P2300R2 std::execution
- P2322R5 ranges::fold
- P2324R1 Labels at the end of compound statements (C compatibility)
- P2327R1 De-deprecating volatile compound operations
- P2347R2 Argument type deduction for non-trailing parameter packs
- P2348R2 Whitespaces Wording Revamp
- P2350R2 constexpr class
- P2361R3 Unevaluated strings
- P2384R1 2021 Spring Library Evolution Poll Outcomes
- P2387R2 Pipe support for user-defined range adaptors
- P2388R3 Minimum Contract Support: either No_eval or Eval_and_abort
- P2400R2 Library Evolution Report: 2021-06-01 to 2021-09-20
- P2408R2 Ranges iterators as inputs to non-Ranges algorithms
- P2417R1 A more constexpr bitset
- P2435R1 2021 Summer Library Evolution Poll Outcomes
- P2445R0 forward_like
- P2447R0 std::span and the missing constructor
- P2448R0 Relaxing some constexpr restrictions
- P2451R0 2021 September Library Evolution Poll Outcomes
- P2460R0 Relax requirements on wchar_t to match existing practices
- P2461R0 Closure-based Syntax for Contracts
- P2463R0 Slides for P2444r0 The Asio asynchronous model
- P2464R0 Ruminations on networking and executors
- P2465R0 Standard Library Modules std and std.all
- P2465R1 Standard Library Modules std and std.compat
- P2466R0 The notes on contract annotations
- P2468R0 The Equality Operator You Are Looking For
- P2469R0 Response to P2464: The Networking TS is baked, P2300 Sender/Receiver is not.
- P2470R0 Slides for presentation of P2300R2: std::execution (sender/receiver)
- P2471R0 NetTS, ASIO and Sender Library Design Comparison
- P2471R1 NetTS, ASIO and Sender Library Design Comparison
- P2472R0 make_function_ref: A More Functional function_ref
- P2473R0 Distributing C++ Module Libraries
- P2475R0 WG21 2021-10 Virtual Meeting Record of Discussion
- P2477R0 Allow programmer to control and detect coroutine elision by static constexpr bool should_elide() and
- P2478R0 _Thread_local for better C++ interoperability with C
- P2479R0 Slides for P2464
- P2480R0 Response to P2471: "NetTS, Asio, and Sender library design comparison" - corrected and expanded
- P2481R0 Forwarding reference to specific type/template
- おわり
採択された文書
ここにあるのは10月の全体会議でワーキングドラフト入りが承認された提案です。ただし、今月提案文書の改定がないものもあるのでこれで全部ではありません。
P0798R8 Monadic operations for std::optional
std::optional
にmonadic interfaceのサポートを追加する提案。
以前の記事を参照
R7(未公開)の変更は
- 機能テストマクロの変更
- 各関数の制約の調整
R8の変更は
transform
の受け取る関数の戻り値型にremove_cv
を適用or_else
から事前条件を削除transform
がコピー省略行えるように調整
などです。
P1147R1 Printing volatile
Pointers
標準ストリームにおいて、volatile
ポインタの<<
による出力をできるようにする提案。
標準出力ストリームを使用してvolatile
ポインタを出力すると、予期しない値が出力されます。
int main() { int* p0 = reinterpret_cast< int*>(0xdeadbeef); volatile int* p1 = reinterpret_cast<volatile int*>(0xdeadbeef); std::cout << p0 << std::endl; // 0xdeadbeef std::cout << p1 << std::endl; // 1 }
標準出力ストリームに対するストリーム出力演算子(<<
)では、const void*
(非volatile
ポインタ)の出力を行うオーバーロードはあり、普通のポインタはこれを使用してアドレスが出力されます。しかし、volatile
ポインタはこのオーバーロードを使用できず(CV修飾が合わないため)、ポインタ->bool
の変換を介してbool
値として出力されます。
この提案は、operator<<(const volatile void*)
のオーバーロードを追加することによってこの意図しない変換を防止し、volatile
ポインタを適切に出力できるようにしようとするものです。
P1272R4 Byteswapping for fun&&nuf
バイトスワップ(バイトオーダー変換)のための関数std::byteswap()
の提案。
以前の記事を参照
このリビジョンでの変更は、提案する文言の修正のみです。
P2077R3 Heterogeneous erasure overloads for associative containers
連想コンテナに対して透過的な要素の削除と取り出し方法を追加する提案。
以前の記事を参照
- P2077R1 Heterogeneous erasure overloads for associative containers - [C++]WG21月次提案文書を眺める(2020年9月)
- P2077R2 Heterogeneous erasure overloads for associative containers - [C++]WG21月次提案文書を眺める(2020年12月)
このリビジョンでの変更は、LWGのフィードバックに基づいて提案する文言を修正したことです。
P2314R4 Character sets and encodings
規格文書中の ~ character setという言葉を明確に定義し直す提案。
以前の記事を参照
- P2314R0 Character sets and encodings - [C++]WG21月次提案文書を眺める(2021年02月)
- P2314R1 Character sets and encodings - [C++]WG21月次提案文書を眺める(2021年03月)
- P2314R2 Character sets and encodings - [C++]WG21月次提案文書を眺める(2021年05月)
- P2314R3 Character sets and encodings - [C++]WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は
- P1949の変更を適用する形で更新
- CWGなどからのフィードバックによる提案する文言の改善
などです。
P2415R2 What is a view?
view
コンセプトの要件を緩和する提案。
以前の記事を参照
- P2415R0 What is a view? - WG21月次提案文書を眺める(2021年07月)
- P2415R1 What is a view? - WG21月次提案文書を眺める(2021年08月)
このリビジョンでの変更は提案する文言の修正のみです(多分)。
P2418R2 Add support for std::generator-like types to std::format
std::generator
-likeな型に対する<format>
のサポートを追加する提案。
以前の記事を参照
- P2418R0 Add support for std::generator-like types to
std::format
- WG21月次提案文書を眺める(2021年08月) - P2418R1 Add support for std::generator-like types to
std::format
- WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は、提案する文言の改善(主にFormatter要件を要求するところの調整)です。
P2432R1 Fix istream_view
, Rev 1
std::views::istream_view<T>()
の他のview
との非一貫性を正す提案。
以前の記事を参照
このリビジョンでの変更は例の修正と、提案する文言の修正です。
P2450R0 C++ Standard Library Issues to be moved in Virtual Plenary, Oct. 2021
今回の会議で採択された標準ライブラリについてのIssue報告とその解決。
- 2191. Incorrect specification of
match_results(match_results&&)
- 2381. Inconsistency in parsing floating point numbers
- 2762. unique_ptr
operator*()
should be noexcept - 3121. tuple constructor constraints for
UTypes&&...
overloads - 3123. duration constructor from representation shouldn't be effectively non-throwing
- 3146. Excessive unwrapping in
std::ref/cref
- 3152.
common_type
andcommon_reference
have flaws in common - 3293. move_iterator
operator+()
has incorrect constraints - 3361.
safe_range<SomeRange&>
case - 3392.
ranges::distance()
cannot be used on a move-only iterator with a sized sentinel - 3407. Some problems with the wording changes of P1739R4
- 3422. Issues of seed_seq's constructors
- 3470. convertible-to-non-slicing seems to reject valid case
- 3480. directory_iterator and recursive_directory_iterator are not C++20 ranges
- 3498. Inconsistent noexcept-specifiers for
basic_syncbuf
- 3535.
join_view::iterator::iterator_category
and::iterator_concept
lie - 3554.
chrono::parse
needsconst charT*
andbasic_string_view<charT>
overloads - 3557. The static_cast expression in
convertible_to
has the wrong operand - 3559. Semantic requirements of
sized_range
is circular - 3560.
ranges::equal
andranges::is_permutation
should short-circuit forsized_ranges
- 3561. Issue with internal counter in
discard_block_engine
- 3563.
keys_view
example is broken - 3566. Constraint recursion for
operator<=>(optional<T>, U)
- 3567. Formatting move-only iterators take two
- 3568.
basic_istream_view
needs to initializevalue_
- 3570.
basic_osyncstream::emit
should be an unformatted output function - 3571.
flush_emit
should set badbit if the emit call fails - 3572. copyable-box should be fully constexpr
- 3573. Missing Throws element for
basic_string_view(It begin, End end)
- 3574.
common_iterator
should be completely constexpr-able - 3580.
iota_view
's iterator's binaryoperator+
should be improved - 3581. The range constructor makes
basic_string_view
not trivially move constructible - 3585. Variant converting assignment with immovable alternative
- 3589. The const lvalue reference overload of get for subrange does not constrain
I
to becopyable
whenN == 0
- 3590.
split_view::base() const &
is overconstrained - 3592.
lazy_split_view
needs to check the simpleness of Pattern - 3593. Several iterators'
base() const &
andlazy_split_view::outer-iterator::value_type::end()
missingnoexcept
- 3595. Exposition-only classes proxy and postfix-proxy for
common_iterator
should be fully constexpr
P2462R0 Core Language Working Group “ready” issues for the October, 2021 meeting
今回の会議で採択されたコア言語についてのIssue報告とその解決。
- 1249. Cv-qualification of nested lambda capture
- 1724. Unclear rules for deduction failure
- 1726. Declarator operators and conversion function
- 1733. Return type and value for operator= with ref-qualifier
- 2484. char8_t and char16_t in integral promotions
- 2486. Call to noexcept function via noexcept(false) pointer/lvalue
- 2490. Restrictions on destruction in constant expressions
- 2491. Export of typedef after its first declaration
- 2496. ref-qualifiers and virtual overriding
その他の文書
N4896 PL22.16/WG21 agenda: 4 October 2021, Virtual Meeting
2021年10月4日 08:00 (北米時間)に行われたWG21本会議のアジェンダです。
C++23のための3回目の全体会議です。
N4897 WG21 admin telecon meeting: September 2021
WG21の各作業部会の管理者ミーティング。
前回から今回の会議の間のアクティビティの報告がされています。
N4898 WG21 2021-10 Virtual Meeting Minutes of Meeting
2021年10月4日(北米時間)に行われた、WG21全体会議の議事録。
CWG/LWG/LEWGの投票の様子などが記載されています。
N4899 WG21 admin telecon meetings: 2022
次回以降のWG21の各作業部会の管理者ミーティング。
次は2022年1月24日に予定されています。
N4900 WG21 virtual plenary meeting(s): 2022
次回以降のWG21全体会議の予定表。
次は2月7日に予定されています。
N4901 Working Draft, Standard for Programming Language C++
N4902 Editors' Report - Programming Languages - C++
↑の変更点をまとめた文書。
10月の会議で採択された提案とコア言語/ライブラリのIssue解決が適用されています。
P0009R13 MDSPAN
多次元配列に対するstd::span
である、mdspan
の提案。
以前の記事を参照
このリビジョンでの変更は
- P2299R3の内容の適用
- 提案する文言の修正
- LEWGのガイダンスに従って、設計に関する議論を追記
- ランク0
mdpsna
のrequired_span_size
のレイアウト修正 - 少なくとも1つの要素数指定が0である
mdspan
のlayout_stride::required_span_size
を修正 operator[]
を多次元配列アクセスに使用span
への文言の参照を解消layout_stride
にストライドとユニークレイアウトのための変換コンストラクタを追加mdspan
にポインタとエクステントからのコンストラクタを追加- layout policy mappingにnothrow move constructible/assignableの要件を追加
- accessor policyにnothrow move constructible/assignableの要件を追加
- accessor policyにaccessor policy pointerの要件を追加
mdspan/submdspan
が何も例外を投げないという指定を削除
などです。
P0627R6 Function to mark unreachable code
到達不可能なコード領域であることを示す、std::unreachable()
の提案。
以前の記事を参照
このリビジョンでの変更は、提案する文言の機能テストマクロ表現を修正したことです。
この提案はすでにLWGのレビューを終えており、次の全体会議で投票にかけられる予定です。
P1169R3 static operator()
関数呼び出し演算子(operator()
)を、静的メンバ関数として定義できるようにする提案。
以前の記事を参照
- P1169R1
static operator()
- [C++]WG21月次提案文書を眺める(2021年04月) - P1169R2
static operator()
- [C++]WG21月次提案文書を眺める(2021年08月)
このリビジョンでの変更は、機能テストマクロを追加したこと、LWG Issue 3617の変更を前提とした文言の追加です。
P1467R5 Extended floating-point types and standard names
C++コア言語/標準ライブラリに拡張浮動小数点型のサポートを追加する提案。
以前の記事を参照
このリビジョンでの変更は
- 文言と設計に関する文書を個別のセクションに分割
- C言語との互換性に関するドキュメントの追記
- 可変引数関数に渡した時の
double
への昇格を削除 <format>
周りの文言に関する説明を追記(不要だった)- I/Oストリームに、
long double
以下の幅を持つ拡張浮動小数点数型のサポート追加 <charconv>/<cmath>
の文言についての背景の追記- よく知られている浮動小数点数に対応する型エイリアスの名前を、
std::floatN_t
とする事を決定
などです。
P1642R7 Freestanding Library: Easy [utilities], [ranges], and [iterators]
[utility]、<ranges>
、<iterator>
から一部のものをフリースタンディングライブラリに追加する提案。
前回の記事を参照
- P1642R3 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2020年6月)
- P1642R4 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2020年7月)
- P1642R5 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2020年12月)
- P1642R6 Freestanding Library: Easy [utilities], [ranges], and [iterators] - [C++]WG21月次提案文書を眺める(2021年06月)
このリビジョンでの変更は、<atomic>
がフリースタンディングであり続けるように文言を調整したこと、std::unreachable
の追加に関する議論を追記したことです。
この提案では、<utility>
全体をフリースタンディングとしていますが、std::unreachable
は<utility>
に追加される予定のため衝突しています。std::unreachable
をフリースタンディングとすることに対する投票が行われたようですが、反対する人はいなかったようです。
P1673R5 A free function linear algebra interface based on the BLAS
標準ライブラリに、BLASをベースとした密行列のための線形代数ライブラリを追加する提案。
- P1673R3 A free function linear algebra interface based on the BLAS - [C++]WG21月次提案文書を眺める(2021年04月)
- P1673R4 A free function linear algebra interface based on the BLAS - [C++]WG21月次提案文書を眺める(2021年08月)
このリビジョンでの変更は、mdspan
が()
に代わって[]
を使用するようになったため、その変更を反映したことです。
どうやらこの提案はC++26を目指すことになったようです。
P1854R1 Conversion to literal encoding should not lead to loss of meaning
文字列リテラルのエンコーディングを実行時エンコーディングに変換する際、文字表現が失われる場合をコンパイルエラーとする提案。
文字列リテラルのエンコーディング(ソースコードのエンコーディング)と実行時エンコーディングが異なるケースは比較的よく発生し、その時の振る舞いは実装定義とされています。
#include <cstdio> int main() { puts("こんにちは"); }
このコードをUTF-8で保存し、実行時エンコーディングをAsciiとしてコンパイルすると、MSVCでは警告のみでコンパイルが通るのに対してGCCではエラーとなり、その際MSVCはAscii内の代替文字(?
)で文字を置換しています。
この事はC++プログラムの移植性を損ねており、文字列は意味や目的を持つ文書であるので実装がその意味を変えるべきではなく、このようなエンコーディングの縮小変換が起こる場合をill-formedと規定しようとする提案です。
この提案ではまた、複数の文字によって1文字を構成するユニコード文字(é
、🇯🇵
など)がマルチキャラクタリテラルとして読み取られて意図しない1文字になる場合をエラーとする事も同時に提案しています。
int main() { [[maybe_unused]] char c = '🇯🇵'; }
マルチキャラクタリテラルに含まれる文字は基本リテラル文字集合の要素のみ、と規定する事でこのような文字がコンパイルエラーととなるようにします。これによって、結合文字などの不可視文字が排除されるため見た目の曖昧さが解消され、マルチキャラクタリテラルの結果がint
に収まるようになります。
P1885R8 Naming Text Encodings to Demystify Them
システムの文字エンコーディングを取得し、識別や出力が可能なライブラリを追加する提案。
以前の記事を参照
- P1885R3 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2020年9月)
- P1885R4 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2020年11月)
- P1885R5 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2021年02月)
- P1885R6 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2021年08月)
- P1885R7 Naming Text Encodings to Demystify Them - [C++]WG21月次提案文書を眺める(2021年08月)
このリビジョンでの変更は
wide_environment()
に関するドキュメントの追加- 実装が任意のエンコーディングをこのライブラリに追加する方法が実装定義であることを明確にした
- 提案する文言に推奨方法を追加
- リテラルのエンコーディングはオブジェクト表現のエンコーディングであることを指定
- エンコーディングとは、エンコーディングスキームであることを指定
- UTF-16LE/BEを常にUTF-16にマップする
- 例の修正
- 提案する文言の修正
などです。
P2012R2 Fix the range-based for loop, Rev2
現在のrange-based forに存在している、イテレーション対象オブジェクトの生存期間にまつわる罠を修正する提案。
以前の記事を参照
- P2012R0 Fix the range-based for loop, Rev0 - [C++]WG21月次提案文書を眺める(2020年11月)
- P2012R1 Fix the range-based for loop, Rev1 - [C++]WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は、提案する文言の改善などです。
P2066R10 Suggested draft TS for C++ Extensions for Minimal Transactional Memory
現在のトランザクショナルメモリTS仕様の一部だけを、軽量トランザクショナルメモリとしてC++へ導入する提案。
以前の記事を参照
- P2066R2 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年05月)
- P2066R3 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年09月)
- P2066R4 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2020年10月)
- P2066R5 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年02月)
- P2066R6 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年03月)
- P2066R7 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年05月)
- P2066R8 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年07月)
- P2066R9 Suggested draft TS for C++ Extensions for Minimal Transactional Memory - [C++]WG21月次提案文書を眺める(2021年09月)
このリビジョンの変更点は、LWGのフィードバックに対応して文言を修正した事です。
この提案は全体会議で承認され、「Minimal Transactional Memory TS」として発行されることになります。
P2248R2 Enabling list-initialization for algorithms
値を指定するタイプの標準アルゴリズムにおいて、その際の型指定を省略できるようにする提案。
以前の記事を参照
- P2248R0 Enabling list-initialization for algorithms - [C++]WG21月次提案文書を眺める(2020年11月)
- P2248R1 Enabling list-initialization for algorithms - [C++]WG21月次提案文書を眺める(2020年12月)
このリビジョンでの変更は
- 理論的根拠について詳しく追記
- 出力イテレータを使用するアルゴリズムのデフォルト
value_type
についての説明の追記 - テスト実装のリンク追加
*_scan
系数値アルゴリズムについてのデフォルトを修正
などです。
P2249R2 Mixed comparisons for smart pointers
スマートポインターの比較演算子に生ポインタとの直接比較を追加する提案。
以前の記事を参照
- P2249R0 Mixed comparisons for smart pointers - [C++]WG21月次提案文書を眺める(2021年02月)
- P2249R1 Mixed comparisons for smart pointers - [C++]WG21月次提案文書を眺める(2021年07月)
このリビジョンでの変更は、提案する文言の修正です。
P2255R2 A type trait to detect reference binding to temporary
一時オブジェクトが参照に束縛されたことを検出する型特性を追加し、それを用いて一部の標準ライブラリの構築時の要件を変更する提案。
以前の記事を参照
- P2255R0 A type trait to detect reference binding to temporary - WG21月次提案文書を眺める(2020年11月)
- P2255R1 A type trait to detect reference binding to temporary - WG21月次提案文書を眺める(2021年04月)
このリビジョンでの変更は、機能テストマクロの追加、std::make_from_tuple
をこの提案に沿って修正、提案する文言の変更などです。
P2264R1 Make assert() macro user friendly for C and C++
↓
P2264R2 Make assert() macro user friendly for C and C++
assert
マクロをC++の構文に馴染むように置き換える提案。
R1での変更は、ローカルなテスト実装を追記したこと、CとC++の提案する文言を分離したこと、フィードバックを受けた議論を追加したことです。
R2での変更は、さらに議論を追記したことなどです。
この提案はCとC++の両方に対して提出されていますが、Cの方の投票では一旦否決されているようです。
P2291R3 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header
std::to_chars, std::from_chars
を整数変換に関してconstexpr
にする提案。
以前の記事を参照
- P2291R0 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header - [C++]WG21月次提案文書を眺める(2021年02月)
- P2291R1 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header - [C++]WG21月次提案文書を眺める(2021年05月)
- P2291R2 Add Constexpr Modifiers to Functions to_chars and from_chars for Integral Types in Header - [C++]WG21月次提案文書を眺める(2021年08月)
このリビジョンでの変更は、ヘッダの肥大化防止やコンパイル時間削減のためコンパイラ組み込み命令で実装されうることについて追記したことです。
P2300R2 std::execution
P0443R14のExecutor提案を置き換える、任意の実行コンテキストで任意の非同期処理を構成・実行するためのフレームワークおよび非同期処理モデルの提案。
以前の記事を参照
このリビジョンでの変更は
- 即時実行
sender
アルゴリズムの削除 connect
カスタマイゼーションポイントとsender_traits<>
を拡張して、awaitable
(コルーチンPromise型)をtyped_sender
として扱えるようにしたas_awaitable(), with_awaitable_senders<>
を追加して、コルーチン型がコルーチン内でsender
を待機可能にしたsender/awaitable
の相互作用について説明を追記sender/reciever
のキャンセルサポートの設計についての説明を追記- 単純な
sender
アダプタアルゴリズムの例を示すセクションを追加 - 単純な
scheduler
の例を示すセクションを追加 - 数独ソルバ、並行再帰ファイルコピー、エコーサーバー、の例を追記
bulk
アルゴリズムのforward progress保証を改善- 様々な
sender
を使用して非同期シーケンスを表現する方法を説明するセクションの追加 sender
を使用して、部分的に成功を表す方法を説明するセクションの追加sender
ファクトリjust_error, just_done
を追加sender
アダプタdone_as_optional, done_as_error
sender/reciever
の製品
などです。
今月の文書にもやりあっているのが見られますが、NetworkingTSのベースとなるExecutorとしてこの提案のscheduler
がふさわしくないことから、議論が紛糾しているようで、進捗が芳しくなさそうです・・・。
P2322R5 ranges::fold
range
アルゴリズムであるranges::fold
の提案。
以前の記事を参照
- P2322R0 ranges::fold - [C++]WG21月次提案文書を眺める(2021年02月)
- P2322R1 ranges::fold - [C++]WG21月次提案文書を眺める(2021年03月)
- P2322R2 ranges::fold - [C++]WG21月次提案文書を眺める(2021年04月)
- P2322R3 ranges::fold - [C++]WG21月次提案文書を眺める(2021年06月)
- P2322R4 ranges::fold - [C++]WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は、プロジェクションを削除したこと、foldl_while
系の操作を削除したこと、全てのオーバーロードがプロジェクションを取らないようになったこと、各関数の名前をfold_left, fold_left_first, fold_right, fold_right_last
に暫定的に変更したことなどです。
プロジェクションは、初期値を最初の要素から取得するタイプ(fold_left_first/fold_right_last
)の場合に、プロクシオブジェクトを返すイテレータ(例えばzip
)を適切に扱うには、余分なコピーが避けられない問題を回避するために削除されました。
このリビジョンの提出後、名前はfold_left, fold_left_first, fold_right, fold_right_last
で正式に決定し、LEWGの投票で合意が取れればLWGに転送される予定です。
P2324R1 Labels at the end of compound statements (C compatibility)
複合ステートメント(compound statement)の末尾にラベルを置けるようにする提案。
このリビジョンでの変更は、3つ提案していた文言を1つに絞ったこと、SG22での投票結果を記載したこと、実装経験について追記したこと、Cと同様の暗黙のnullステートメントを追加できるようにしたこと、などです。
P2327R1 De-deprecating volatile compound operations
非推奨となったvolatile
値に対する複合代入演算子を再考する提案。
このリビジョンでの変更は、bitwise演算子の複合代入演算子(|= &= ^=
)の非推奨解除のみを提案するようにしたことです。
この提案はEWGの議論を通過し、CWGに送られるためのEWGの投票待ちです。なお、C++20へのDRとなるようです。
P2347R2 Argument type deduction for non-trailing parameter packs
関数テンプレートの実引数型推論時に、引数リスト末尾にないパラメータパックの型を推論できるようにする提案。
以前の記事を参照
- P2347R0 Argument type deduction for non-trailing parameter packs - WG21月次提案文書を眺める(2021年06月)
- P2347R1 Argument type deduction for non-trailing parameter packs - WG21月次提案文書を眺める(2021年07月)
このリビジョンでの変更は、テスト実装を使用してより広いコンテキスト(クラス、関数テンプレートのアドレス取得、エイリアステンプレート、部分特殊化)でこの推論が上手くいくかの調査の結果を追記したことです。調査では、特に問題は見つからなかったようです。
P2348R2 Whitespaces Wording Revamp
ユニコードの仕様に従う形で、改行と空白を明確に定義する提案。
以前の記事を参照
- P2348R0 Whitespaces Wording Revamp - [C++]WG21月次提案文書を眺める(2021年04月)
- P2348R1 Whitespaces Wording Revamp - [C++]WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は、提案する文言の修正です。
この提案はSG16とEWGのレビューを通過し、CWGへ送るためのEWGの投票待ちをしています。
P2350R2 constexpr class
constexpr
対応クラスの簡易構文の提案。
以前の記事を参照
- P2350R0 constexpr class - WG21月次提案文書を眺める(2021年04月)
- P2350R1 constexpr class - WG21月次提案文書を眺める(2021年07月)
このリビジョンでの変更は、例を追加したこと、EWGでの投票の結果を記載したこと、constexpr
クラスのstatic
メンバ変数はconstexpr
となることを明確にしたこと、constexpr(false)
が必要な理由と不要な理由を記載したこと、などです。
メンバ関数の一部がCのAPIに依存していたりするとその関数はconstexpr
化不可能ですが、そのクラスをconstexpr
クラスにしてしまうとそのようなメンバ関数が1つあるだけでエラーとなってしまいます。そのため、選択的に非cosntexpr
にするためにconstexpr(false)
が必要だ、というフィードバックが寄せられたようです。ただし、その場合でもクラス外で定義することで回避可能であったり、P2448R0が採択されればその問題を回避できるとして、この提案ではconstexpr(false)
を提案していません。
P2361R3 Unevaluated strings
コンパイル時にのみ使用され、実行時まで残らない文字列リテラルについての扱いを明確化する提案。
以前の記事を参照
- P2361R0 Unevaluated string literals - [C++]WG21月次提案文書を眺める(2021年04月)
- P2361R1 Unevaluated string literals - [C++]WG21月次提案文書を眺める(2021年06月)
- P2361R2 Unevaluated string literals - [C++]WG21月次提案文書を眺める(2021年08月)
このリビジョンでの変更は
- プリプロセッシングトークンはコンテキストに依存してはならないことからunevaluated-stringをプリプロセッシングトークンとしないように文言を改善した
- 文字列リテラルの評価中にその文字列をnull終端することで、unevaluated-stringがnull終端されていないことを明確化
- literal-operator-idの文言にunevaluated-stringを適用
extern
の文言にunevaluated-stringを適用し、リンケージ指定がユニコード文字を示すことを明確化asm
ステートメントでの数値エスケープシーケンスの許可
などです。
P2384R1 2021 Spring Library Evolution Poll Outcomes
2021年の春(4月から6月にかけて)に行われた、LEWGの全体投票の結果。
以前の記事を参照。
このリビジョンの変更は引用する文書が間違っていたのを修正しただけです。
P2387R2 Pipe support for user-defined range adaptors
ユーザー定義のRangeアダプタに対して、パイプライン演算子(|
)サポートを簡単に行えるユーティリティを提供する提案。
以前の記事を参照
- P2387R0 Pipe support for user-defined range adaptors - WG21月次提案文書を眺める(2021年06月)
- P2387R1 Pipe support for user-defined range adaptors - WG21月次提案文書を眺める(2021年06月)
このリビジョンでの変更は、もう一つ機能テストマクロを追加した事です。
この提案はSG9とLEWGのレビューを終え、LWGに送られるための投票待ちをしています。C++23入りを目指しています。
P2388R3 Minimum Contract Support: either No_eval or Eval_and_abort
契約が破られた時に継続しないコントラクトサポートを追加する提案。
以前の記事を参照
- P2388R0 Abort-only contract support - WG21月次提案文書を眺める(2021年05月)
- P2388R1 Minimum Contract Support: either Ignore or Check_and_abort - WG21月次提案文書を眺める(2021年08月)
- P2388R2 Minimum Contract Support: either Ignore or Check_and_abort - WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は
- 構文について、属性との混同、Cとの潜在的な非互換性について追記
- 契約モード名をNo_eval、Eval_and_abort*に変更
- "function parameter"とすべきところを"function argument"としていたところを修正
- 事後条件で非
const
非参照仮引数を使用できるようにする解決策の案として、暗黙const
扱いにする例を追加 - セキュリティの懸念に対処するため、
violation handler
が標準出力にメッセージを出力することを推奨する文言を削除 - 事後条件で参照されている関数引数を
const_cast
した場合に未定義動作となる説明を追記 - 契約条件部分式ごとの副作用削除についての議論の追記
- 実装可能性についてのセクションを追加
- 契約違反時に
std::terminate()
ではなく、std::abort()
を呼び出す理由について追記 - 契約条件がimmediate contextにないことを明確にした
- 構文の選択、副作用の説明、プログラミングモデルの有効性、に関する未解決の問題の追加
などです。
P2400R2 Library Evolution Report: 2021-06-01 to 2021-09-20
2021年6月から9月にかけての、LEWGでのミーティングについてのまとめ。
どれくらいミーティングを行ったのか、おおまかな機能単位についての進行状況、レビューを行った提案についての議論の状況などが記載されています。
P2408R2 Ranges iterators as inputs to non-Ranges algorithms
非Rangeアルゴリズムのイテレータに対する名前付き要件を、イテレータコンセプトで置き換える提案。
以前の記事を参照
- P2408R0 Ranges views as inputs to non-Ranges algorithms - WG21月次提案文書を眺める(2021年07月)
- P2408R1 Ranges views as inputs to non-Ranges algorithms - WG21月次提案文書を眺める(2021年09月)
このリビジョンでの変更は、タイトルの変更、input iteratorとoutput iteratorの要件についてイテレータコンセプトを使用するようにしていた部分の削除、mutable iteratorに関する議論の追加、zip_view
のイテレータなどについての説明の追記。
この提案はSG9のレビューを通過し、LEWGへ転送されました。
P2417R1 A more constexpr bitset
std::bitset
をconstexpr
対応させる提案。
以前の記事を参照
このリビジョンでの変更は、設計についての議論と実装経験についてを追記したことです。
MSVC STLのbitset
実装をベースにcosntexpr
化を行い、実装できることと、既存のテストをパスすることを確かめたようです。
P2435R1 2021 Summer Library Evolution Poll Outcomes
2021年の夏(7月から9月にかけて)に行われた、LEWGの全体投票の結果。
以前の記事を参照。
このリビジョンの変更は引用する文書が間違っていたのを修正しただけです。
P2445R0 forward_like
クラス型のメンバ変数について、const
性も含めた正しい完全転送を行うstd::forward_like
の提案。
これはDeducing Thisによって頻出するであろうコードを正しく書くためのユーティリティです。
template<typename T> struct wrap { T v; template<typename Self> auto value(this Self&& self) -> decltype(auto) { return std::forward<Self>(self).v; // あってる? } };
この.value()
メンバ関数はthis
(self
)の値カテゴリに応じてメンバv
の返す型(値カテゴリ)を変化させたいわけです。C++20まではstd::optional
の.value()
のようにconst
と参照修飾によって4つのオーバーロードに分けていましたが、C++23以降はDeducing Thisによってthis
に対応する引数を明示的に取れるようになった事から1つの関数にまとめることができるようになります。
その際、this
の状態によってメンバを完全転送するにはおおよそ上記のように書くことになるのですが、メンバが参照である場合やthis
がconst
である場合に正しい結果になりません。次の表は上記のように書いた時の.value()
の戻り値型がthis
の状態とメンバの状態によってどうなるかを示したものです
this (self ) |
メンバ(v ) |
std::forwad(self).v |
---|---|---|
&& |
||
& |
& |
|
&& |
&& |
|
const |
const && |
|
const & |
const & |
|
const && |
const && |
|
const |
const && |
|
& |
const |
const & |
&& |
const |
const && |
const |
const |
const && |
const & |
const |
const & |
const && |
const |
const && |
& |
& |
|
& |
& |
& |
&& |
& |
& |
const |
& |
& |
const & |
& |
& |
const && |
& |
& |
&& |
& |
|
& |
&& |
& |
&& |
&& |
& |
const |
&& |
& |
const & |
&& |
& |
const && |
&& |
& |
const & |
const & |
|
& |
const & |
const & |
&& |
const & |
const & |
const |
const & |
const & |
const & |
const & |
const & |
const && |
const & |
const & |
const && |
const & |
|
& |
const && |
const & |
&& |
const && |
const & |
const |
const && |
const & |
const & |
const && |
const & |
const && |
const && |
const & |
表中の空白はconst
も参照修飾もない状態、すなわち単なる値として宣言されている場合です(this
の場合はコピーされている、すなわちprvlaueの状態)。
これを見ると、std::forward<Self>(self).v
のようなコードが問題ないのは、v
が非参照である時くらいのものです。メンバが参照である場合はthis
のconst
が正しく伝わっておらず、メンバがconst
参照の場合はthis
の値カテゴリ(特に右辺値&&
)が正しく伝播されていません。
std::forward_like
はこの伝播を正しく行い完全転送するためのもので、std::forward
とよく似ています。forward_like
を使うと先ほどのコードは次のように書き換えられます
template<typename T> struct wrap { T v; template<typename Self> auto value(this Self&& self) -> decltype(auto) { return std::forward_like<Self>(self.v); } };
次の表は、先ほどの表にforward_like
の結果を追記したものです
this |
メンバ | forwad |
forward_like |
---|---|---|---|
&& |
&& |
||
& |
& |
& |
|
&& |
&& |
&& |
|
const |
const && |
const && |
|
const & |
const & |
const & |
|
const && |
const && |
const && |
|
const |
const && |
const && |
|
& |
const |
const & |
const & |
&& |
const |
const && |
const && |
const |
const |
const && |
const && |
const & |
const |
const & |
const & |
const && |
const |
const && |
const && |
& |
& |
&& |
|
& |
& |
& |
& |
&& |
& |
& |
&& |
const |
& |
& |
const && |
const & |
& |
& |
const & |
const && |
& |
& |
const && |
&& |
& |
&& |
|
& |
&& |
& |
& |
&& |
&& |
& |
&& |
const |
&& |
& |
const && |
const & |
&& |
& |
const & |
const && |
&& |
& |
const && |
const & |
const & |
const && |
|
& |
const & |
const & |
const & |
&& |
const & |
const & |
const && |
const |
const & |
const & |
const && |
const & |
const & |
const & |
const & |
const && |
const & |
const & |
const && |
const && |
const & |
const && |
|
& |
const && |
const & |
const & |
&& |
const && |
const & |
const && |
const |
const && |
const & |
const && |
const & |
const && |
const & |
const & |
const && |
const && |
const & |
const && |
std::forwad
の時と比較すると、メンバが参照である場合にも正しく(理想的に)const
と値カテゴリが伝播しているのが分かります。Deducing Thisで可能になることを考えればこのようなコードは良く書かれることが予想され、forward_like
の必要性はDeducing Thisの提案中でも指摘されていました。
std::forward_like
は簡単には次のように実装されます。
// T->Uへ参照(値カテゴリ)を伝播する template <typename T, typename U> using __override_ref_t = std::conditional_t<std::is_rvalue_reference_v<T>, std::remove_reference_t<U>&&, U&>; // T->Uへconstをコピーする template <typename T, typename U> using __copy_const_t = std::conditional_t<std::is_const_v<std::remove_reference_t<T>>, U const, U>; // forward_likeの結果型を求める template <typename T, typename U> using __forward_like_t = __override_ref_t<T&&, __copy_const_t<T, std::remove_reference_t<U>>>; template <typename T> [[nodiscard]] constexpr auto forward_like(auto&& x) noexcept -> __forward_like_t<T, decltype(x)> { return static_cast<__forward_like_t<T, decltype(x)>>(x); }
各メタ関数のT
は*this
の、U
はメンバの型が来ます。すなわち、なるべくthis
の値カテゴリを利用してメンバの値カテゴリを指定しつつthis
のconst
を伝播しようとするものです。
値カテゴリによって振る舞いを変化するラムダ式によるコールバックのサンプル
auto callback = [m=get_message(), &scheduler](this auto&& self) -> bool { return scheduler.submit(std::forward_like<decltype(self)>(m)); }; callback(); // retry(callback) std::move(callback)(); // try-or-fail(rvalue)
この例のように、Deducing thisによってキャプチャしているラムダを扱う際にforward_like
は特に重要となります。
間接的な所有権を持つクラスにおける、所有するメンバの転送のサンプル
struct fwd { std::unique_ptr<std::string> ptr; std::optional<std::string> opt; std::deque<std::string> container; auto get_ptr(this auto&& self) -> std::string { if (ptr) { return std::forward_like<decltype(self)>(*ptr); } return ""; } auto get_opt(this auto&& self) -> std::string { if (opt) { return std::forward_like<decltype(self)>(*m); } return ""; } auto operator[](this auto&& self, size_t i) -> std::string { return std::forward_like<decltype(self)>(container[i]); // dequeは右辺値用[]を持たない } };
この例では、ポインタを介した所有などによってthis
のconst
性および値カテゴリの伝播が断ち切られているケースでも、Deducing Thisとforward_like
によって望ましい転送を実現しています。
P2447R0 std::span and the missing constructor
std::span
にinitializer_list
を受け取るコンストラクタを追加する提案。
現在のstd::span
にはstd::initializer_list
を受け取るコンストラクタがなく、次のようなコードがエラーとなります。
void foo(std::span<const int>); int main() { foo({1, 2, 3}); // error }
この提案はstd::span
にはstd::initializer_list
を受け取るコンストラクタを追加してこれがコンパイルできるようにしようとするものです。
この提案のモチベーションは、従来std::vector
を利用していた関数引数をstd::span
に置き換えた時に発生する問題を解決することにあります。
// C++17までのインターフェース void foo(const std::vector<int>&); // C++20からはspanを使い書き換えたい void foo(std::span<const int>); int main() { foo({1, 2, 3}); // C++17まではOK、C++20からはエラー }
この変更が適用されると{}
によって簡単にダングリングstd::span
を作れてしまうようになりますが、現状でもダングリングspan
は割と簡単にできてしまい、std::span
がview
である以上それは避けられないため、std::initializer_list
を取れるようにすることのメリットの方が大きいという主張です。
また、std::initializer_list
コンストラクタは{}
初期化時に最優先で選択されることから、この変更は破壊的変更となります。しかし、std::initializer_list
コンストラクタに制約を設けることでこの影響は低減でき、破壊的となるのは非常にレアケースとなるようです。
const std::vector<int>; // この変更の後でも、これらの振る舞いは変化しない auto sp1 = std::span{v}; auto sp2 = std::span{v.begin(), v.end()}; auto begin = v.data(); auto end = begin + v.size(); auto sp3 = std::span{begin, end}; // この提案以前はvoid*1つのspan // この提案の後では、void*2つのspan void* vp = nullptr; span<void* const> sp4{&vp, &vp+1}; // この提案以前はany1つのspan // この提案の後では、any2つのspan std::any a; std::any* ap = &a; span<std::any> sa{&ap, &ap+1};
std::span
の主な用途の一つは関数引数で使用することです。その場合、span
の要素型をテンプレートパラメータにすることはできないためspan
の要素型は明示的に指定されているはずです。従って、void*
などの特殊な要素型を考慮する必要は通常ないため、破壊的変更はやはり問題とならないとのことです。
P2448R0 Relaxing some constexpr restrictions
constexpr
関数がすべての引数について定数実行不可能となる場合でも、コンパイルエラーにしないようにする提案。
例えば、次のコードはC++20まではエラーとなりますが、C++23以降(P2231R1実装後)は定数実行可能となるためエラーになりません。
constexpr void h(std::optional<int>& o) { o.reset(); // C++20まで非constexpr }
現在の仕様では、constexpr
関数がそのあらゆる引数について定数実行可能ではない場合コンパイルエラー(もしくは診断不要のill-formed)とする事が求められています。そのため、constexpr
関数内で非constexpr
関数を呼び出す場合は常にコンパイルエラーとなります。
これを適応的にコンパイルするため、機能テストマクロを使用して次のようにすることができます
#if __cpp_lib_optional >= 202106 constexpr #endif void h(std::optional<int>& o) { o.reset(); }
しかし、この方法は適切でしょうか?
std::optional
のこの例に見えるように、ある関数がある時点でconstexpr
ではなかったとしても将来的にconstexpr
になる可能性があります。constexpr
のルールはC++のバージョンごとに拡張されているため、それまでconstexpr
対応できなかった処理でも、あるバージョンからはconstexpr
化することができるようになり得ます。しかし、標準ライブラリのサポートはそれが可能になったタイミングから遅れてそれに対応し、在野のライブラリはさらに遅れるのが常です。またこのような機能テストマクロが非標準のものについて提供されることはほぼなく、在野のライブラリを使用している場合、このような対応は手作業で行う必要があります。
このような診断は定数式で実行可能なものが大きく制限されていたC++11の時代には有用だったのかもしれませんが、より多くのものが定数式で実行可能となった現在(C++23)の環境には即しておらず、constexpr
での実行可否の診断は実際に定数式で実行する時にまで遅延しておく方が良いでしょう。この提案は、そのような規定を取り除こうとするものです。
P2451R0 2021 September Library Evolution Poll Outcomes
2021年の秋に行われた、LEWGの全体投票の結果。
以下の5つの提案が投票にかけられ、LWGに転送されることが可決されています。また、その際に寄せられたコメントが記載されています。
- P2418R0 Add Support For
std::generator
-like Types Tostd::format
- P2415R1 What Is A view?
- P2432R0 Fix istream_view
- P2351R0 Mark All Library Static Cast Wrappers As
[[nodiscard]]
- P2291R2 Add Constexpr Modifiers To Functions
to_chars
Andfrom_chars
For Integral Types In<charconv>
Header
P2460R0 Relax requirements on wchar_t to match existing practices
wchar_t
のエンコーディングについての実態になじまない制約を取り除く提案。
現在のC++標準におけるwchar_t
のエンコーディングに関する規定は、ワイド文字エンコーディングのすべての文字を単一のコード単位としてエンコーディングする必要がある(すなわち、すべての文字がwchar_t
1つに収まらなければならない)ことを要求しています。例えばWindowsではそれはUTF-16であり、UTF-16はサロゲートペアを持つためその要件に違反していることになります。
さらに、wchar_t
の値はサポートされているロケールで指定された拡張文字セットの個別の文字を表現できることも要求されているため、実行字文字集号がUTF-8の場合はすべてのユニコード文字をwchar_t
1つで表現可能であることが求められ、実質的に2バイトエンコーディングを排除しています。
これらのことは実際の実装(主にWindows)に沿っておらず、C++を多様なOS上での開発に適さないものにしてしまっています。そのため、実装の慣行に従う形でこれを修正するのがこの提案の目的です。
この提案による変更は実装ですでに行われていることを標準化するだけなので、実装及びプログラマに影響はありません。
P2461R0 Closure-based Syntax for Contracts
属性likeな構文に代わるコントラクト構文の提案。
提案されている構文はpre, post, assert
の3つの文脈依存キーワードに続いて、ラムダ式(に近い構文)によって契約条件を記述します。pre/post
は後置戻り値型の後ろのrequires
節と同じ位置に書き、assert
は関数本体内に書きます。
auto plus(auto const x, auto const y) -> decltype(x + y) pre { x > 0 } pre { // オーバーフローのチェック // ここに書けるものは条件式だけなので、&&で結合する (x > 0 && y > 0 ? as_unsigned(x) + as_unsigned(y) > as_unsigned(x) : true) && (x < 0 && y < 0 ? as_unsigned(x) + as_unsigned(y) < as_unsigned(x) : true) } // retはauto&&と宣言したかのような引数、戻り値が渡される post (ret) { ret == (x + y) } { assert { x > 0 }; // この構文は現在有効(assert構造体の集成体初期化) auto cx = x; return cx += y; }
ただし、この提案は純粋に構文のみを提案するもので、P2388の意味論をほぼ踏襲しており、P2388およびMVP(Minimum Viable Product)と呼ばれるコントラクトの最小セットに異を唱えるものではありません。この提案は、P2388で示されているいくつかの問題を解決可能な構文を探索し、提案するものです。
構文定義は次のようになっています
correctness-specifier: correctness-specifier-keyword correctness-specifier-introducer(opt) correctness-specifier-body correctess-specifier-keyword: pre | post | assert correctness-specifier-introducer: lambda-introducer(opt) return-value-id(opt) return-value-decl: ( identifier ) correctness-specifier-body: { conditional_expression }
pre, post, assert
の後にはラムダ式(のような)構文を置くことができて、ラムダキャプチャも機能します。省略された場合は[&]
から始まったものとみなされています。ただし、現在合意が取れているMVPと呼ばれるコントラクトの最小セットに従う場合、ラムダ導入子は常に省略されなければなりません。
correctess-specifier-keyword
に続く{}
の中には条件式のみを書くことができて、これはrequires
節と同じです。
ラムダ導入子を許可しなかったとしても、この構文ではP2388で導入される未定義動作を減らすことができるようです。
ラムダ導入子を許可すると、コピーキャプチャによってP2388に存在する事後条件で非const
引数を参照できないという問題を解決することができます。これは事後条件から参照される実引数がタイミングによって変化しうることで条件の意味が変わってしまうことを防止するために現在(MVP)では禁止されています。
- P2388R0 Abort-only contract support - WG21月次提案文書を眺める(2021年05月)
- P2388R2 Minimum Contract Support: either Ignore or Check_and_abort - WG21月次提案文書を眺める(2021年09月)
- P2461 進行状況
P2463R0 Slides for P2444r0 The Asio asynchronous model
P2444の解説スライド。
ASIOの非同期モデルとExecutor、非同期処理グラフの構成などが作者自らによって解説されています。
P2464R0 Ruminations on networking and executors
P0443のExecutorを使用するNetworking TSを標準化すべきではないという提案。
この提案で問題とされているのはNetworking TSの完了ハンドラがExecutorそのものがエラーを起こさないと仮定している事、およびP0443のexecutor
にエラーや処理の成否を通知するチャネルが無いこと、Networking TSのExecutorと完了ハンドラによる非同期モデルはsender/reciever
と比較するとジェネリックでも構成可能でも無いことです。
Networking TS(ASIO)の非同期処理ではI/O処理の継続作業を完了ハンドラで行います。完了ハンドラではAssociated Executorを取得し利用することでそのI/O処理や完了ハンドラの実行されているコンテキストに継続作業を投入することができます。このAssociated ExecutorはP0443のexecutor
ではありますがscheduler
ではなく、処理のスケジューリングに伴うエラーや、先行する処理の失敗を継続処理に通知する方法がありません。そして、Associated Schedulerのようなものを取得するAPIはなく、非同期処理グラブの構成もその場しのぎ的な書き方しかできません。また、実行コンテキストをAssociated Executor以外のExecutorのコンテキストに変更する方法もありません。
Executorライブラリの導入後、幾つものExecutorが作成されることになり、そこではExecutorの表現力不足に伴う問題を解決する事を繰り返し行う必要が出てきます。Executorはワークランナーの抽象化でしかなく、汎用的なエラー処理/検出の方法を提供するものではありません。従って、Networking TSのおよびP0443のexecutor
とは、scheduler
である必要があります。scheduler
はexecutor
と同等の能力を持ちながら、reciever
による成否とスケジュールエラーの通知チャネル、sender
による非同期処理グラフのジェネリックな構成をサポートしています。
その場合、Networking TSの非同期処理は完了ハンドラの代わりにsender
を受け取るようになるでしょう。sender
はあらかじめ構成された非同期処理のグラフを示す非同期エージェントであり、Networking TSから提供されるscheduler
をそのまま使用することも、別のscheduler
に実行コンテキストをスイッチすることもできます。さらに、sender
による構成では、前の処理およびスケジューリング段階でエラーが発生している場合後続の処理は実行されず、そのエラーをハンドリングする方法が提供されます。そして、それらの処理はジェネリックに構成することができ、特定のscheduler
および特定の継続操作に依存せず、完了ハンドラのようにアドホック的ではありません。
// 擬似コードによる非同期I/Oと継続の例 void run_that_io_operation(scheduler auto sched, sender_of<network_buffer> auto wrapping_continuation) { // 特定のI/O操作 launch_our_io(); // I/O結果の取得 auto [buffer, status] = fetch_io_results_somehow(maybe_asynchronously); // ユーザーの指定した継続を、ユーザーの指定したschedulerで実行する run_on(sched, queue_onto(wrapping_continuation, make_it_queueable(buffer, status))); }
ここでは、scheduler
の具体的な型もsender
(wrapping_continuation
)の具体的な型も知る必要はありません。この関数の作者が詳しく知る必要があることはI/O操作に関することのみです。継続ラッパ(wrapping_continuation
)が何をするかも知る必要がなく、それおよびそれがどこで実行されるのかを決めるのはこの関数を利用するユーザーです。
ユーザーはsender
の先に自由に継続を構成でき、そのユーザーがラップした処理を使用する別のユーザーもまた同様の方法によって継続をカスタマイズすることができます。この関数の作者は上位のユーザーが何をしたのかを知ることなくI/O処理を実行し、scheduler
にsender
を投入でき、上位ユーザーはI/Oが何をしたのか継続をどう実行したのかを知ることなく、その結果から変換された指定した継続を実行することができます。
多数の実行戦略(executor/scheduler
)とさらに多数の継続(完了ハンドラやsender
)をサポートするために、Networking TSは多数のもの(追加の関数の変種)を必要としますがP2300(sender/reciever
)では1つだけですみます。
P0443のexecutor
はこれができません。executor
はあらゆる種類の失敗やエラーを通知する方法を持ちません。そして、Networking TSでの入力実行戦略はexecutor
のみです。
この提案が示しているのは次の2つの方向性です。
- Networking TSのExecutorモデルをP0443からP2300へ変更する
- C++23 Networking TSの標準化よりも、
sender/reciever
(P2300)の開発に集中する- ネットワークライブラリはその完成後にそれにフィットするものを標準化する
筆者の方は2つ目を推しているようです。
P2465R0 Standard Library Modules std and std.all
↓
P2465R1 Standard Library Modules std and std.compat
標準ライブラリモジュールについて最小のサポートをC++23に追加する提案
これは以前のP2412R0を受けて、具体的にその内容を詰めるものです。
P2412R0のLEWGのレビューにおいて問題となった最大の事項は、import std;
(std
モジュール)がstd
名前空間ではなくグローバル名前空間にあるものを含むかどうかという点でした。現在のC++標準では、<meow.h>
は::meow
を確実に提供しstd::meow
を提供するかは分かりません、<cmeow>
は::meow
とstd::meow
の両方を確実に提供します。これは説明も対処も厄介な問題であり、またグローバル名前空間の暗黙の汚染をもたらします。標準ライブラリモジュールはこの問題を解決する唯一の機会だと言えます。
ただ、std
モジュールがグローバル名前空間にあるものを提供しない場合、既存のコードの標準モジュール対応が妨げられてしまいます。
そこでこの提案では、2つの標準ライブラリモジュールを提供することでユーザーに選択肢を与え、この問題に対処します。
import std;
import std.compat;
std
+Cライブラリヘッダの部分をインポートする。C由来のものがグローバル名前空間にインポートされる。既存コードベースの移行時などimport std;
よりも重くなる可能性があるが、C互換ヘッダはサイズが小さいためそこまで問題にならないはず
std
とstd.compat
の違いはグローバル名前空間に何かをインポートするかどうかだけです。std
モジュール使用時、グローバル名前空間は清浄に保たれます。例えば、<cmath>
はstd
名前空間にもグローバル名前空間にもほぼ同じオーバーロードが定義されますが、import std;
の場合はstd::~
だけが使用可能となり、import std.compat;
の時はそれに加えて::~
のものも使用可能になります。
この提案は現在の内容でLWGに転送するために、次のLEWG全体投票にかけられます。
P2466R0 The notes on contract annotations
C++契約プログラミングのビジョンと設計目標について解説した文書。
関数コントラクト(function contract)という概念とコントラクト注釈(contract annotation)の関係を説明し、コントラクト注釈に求められることや役割について説明されています。また、事後条件で関数の非参照const
引数を使用する場合の問題点と解決策の考察が行われています。
P2468R0 The Equality Operator You Are Looking For
==
から導出される!=
演算子とユーザー定義!=
演算子が衝突してしまう問題を解消する提案。
次のコードはコンパイラによって扱いが変化します。
struct S { bool operator==(const S&) { return true; } // (1) 非constメンバ関数 bool operator!=(const S&) { return false; } // (2) 非constメンバ関数 }; int main() { bool b = S{} != S{}; // #1 }
#1
の箇所ではユーザー定義!=
(2)と(1)から導出された!=
の2つが候補として上がります。S
の右辺値との比較でベストマッチものはないのでS
の値への変換がどちらでも行われ、メンバ関数のconst
修飾がなされていないことによってどちらの候補も最適とみなされ、その扱いがコンパイラによって変化しています。
- GCC : (1)を使用する(警告なし)
- 変換シーケンスの前に、パラメータ型が一致する複数の候補がある場合に生成された候補を無視する
- clang : (1)を使用する(警告あり)
- MSVC : (1)を使用する(警告なし)
- 生成された候補を削除する
現在の比較演算子(a @ b
)の自動導出に伴うオーバーロード解決のルールでは、@
そのもの->生成された正順の式(a @' b
)->生成された逆順の式(b @' a
)の順で優先順位が高くなるというルールがあるのですが、このケース(@
は!=
)では変換なしでベストマッチする!=
が存在しないため、定義されているものと生成されたものとで優先順位が同等になってしまっているようです。
コンパイラから見ると先ほどの例は、次のようなコードと同じことをしています
struct S { friend bool f(S, const S&) { ... } friend bool f(const S&, S) { ... } }; bool b = f(S{}, S{});
この提案では、現在の厳格なルールをMSVCにて実装(出荷されているMSVCの実装とは異なる)し、59のコードベースの調査を行なっています。その結果、20のプロジェクトでこの問題によるコンパイルエラーが観測されました。
template <typename T> struct Base { bool operator==(const T&) const; }; struct Derived : Base<Derived> { }; bool b = Derived{} == Derived{};
このケースでは、派生クラスから基底クラスへの変換が発生し、同様の問題に陥っています。これは受け入れられるべきです。
template <bool> struct GenericIterator { using ConstIterator = GenericIterator<true>; using NonConstIterator = GenericIterator<false>; GenericIterator() = default; GenericIterator(const NonConstIterator&); // (1) bool operator==(ConstIterator) const; }; using Iterator = GenericIterator<false>; bool b = Iterator{} == Iterator{};
このケースは、Iterator
(GenericIterator<false>
)の比較を暗黙変換コンストラクタ(1)を通すことによって、ConstIterator
(GenericIterator<true>
)の比較として実行しています。暗黙変換が行われることによって同様の問題が起きています。これは意図的であるので受け入れられるべきです。
struct Iterator { Iterator(); Iterator(int*); bool operator==(const Iterator&) const; // (1) operator int*() const; // (2) }; bool b = nullptr != Iterator{};
このケースは、(2)によってIterator
からポインタへ暗黙変換することで、!=
を提供しようとしています。暗黙変換が行われることによって同様の問題が起きています。しかしこの場合、逆順の==
使用時にも暗黙変換によって組み込み==
が使用されてしまい、より最適なはずのnullptr
からIterator
を構築して==
(1)が使用されないという問題があります。このケースはリジェクトされるべきです。
using ubool = unsigned char; struct S { operator bool() const; }; ubool operator==(S, S); // (1) ubool b = S{} != S{};
これは厳密には先程までの問題と関係がありません。この例がエラーとなるのは、(1)の==
の戻り値型がbool
ではないからです。!=
の導出に使用される==
の戻り値型は(cv)bool
でなければならず、そうでない場合オーバーロード解決で選ばれるとコンパイルエラーとなります。
GCCの実装では、オーバーロード解決において変換を考慮する前に、候補集合の中に同じパラメータ型を持つ候補があり片方が生成された候補である場合に、その生成された候補を以降のオーバーロード解決で無視することで以前の振る舞いを維持しようとしています。このGCC実装をMSVCにも実装して再度テストしたところ、コンパイルに失敗したのは10プロジェクト(-10)となりました(上記の受け入れたい/リジェクトしたいケースは意図通りとなっている)。
この提案では、GCCの実装をベースとして実装の自由度も考慮して、「生成された演算子候補について、同じスコープで宣言された同じシグネチャを持つ別の候補が存在し、その候補が生成されていない(ユーザー定義である)候補である場合、生成された演算子はオーバーロード候補から除外される」、というルールをオーバーロード解決のルールに追加することを提案しています。それによって、最初の例のようなコードは曖昧ではなくなり、上記のアクセプトしたい既存コード例はコンパイルされリジェクトしたいコード例はコンパイルされない、という振る舞いを手に入れることができます。
P2469R0 Response to P2464: The Networking TS is baked, P2300 Sender/Receiver is not.
「P2464R0 Ruminations on networking and executors」の主張に反論する文書。
ASIO(NetworkingTS、以下ASIOとは=NetworkingTSも意味する)の定義する非同期モデルにおけるExecutorとは、非同期処理のtail callのカスタマイゼーションポイントであるという前提を説明し、P2300の抽象化はscheduler
の具体的実装を知っていなければ一般的なアルゴリズムを構成できず、P2300は非同期モデルを定義するものではなく非同期処理グラフを作成するためのDSLに過ぎず、ASIO/NetworkingTSの非同期モデルはP2300のDSLを実装することができるより基礎的なモデルであると主張しています。
P2470R0 Slides for presentation of P2300R2: std::execution (sender/receiver)
P2300の紹介・解説スライド。
P2300のscheduler
とsender/reciever
によるアルゴリズムの紹介とそれによる非同期処理グラフの構成、then
アルゴリズムの実装解説、コルーチンとsender
の親和、sender
におけるキャンセルなどについて解説されています。
また、P2300の実装(libunifex)がFacebookにて広く実運用されているほか、NVIDIAがその実現にコミットしていくことや、Bloomberg社内でも好印象を得ていることが紹介されています。
P2471R0 NetTS, ASIO and Sender Library Design Comparison
↓
P2471R1 NetTS, ASIO and Sender Library Design Comparison
ASIOとNetworkingTSとP2300(P0443)の設計を比較する文書。
それぞれのライブラリのモデルや要求することなどをコンセプトによって表現し、表にまとめて比較しています。
P2472R0 make_function_ref: A More Functional function_ref
function_ref
に適応的に型消去させるためのヘルパ関数make_function_ref()
を追加する提案。
function_ref
は関数呼び出し可能なものに対するview
であり、主として関数引数で任意のCallableなものを受け取るときに使用します。そこでは、テンプレートによる受け取りとstd::function
による受け取りのいいとこ取りのような性質を利用できます。
// テンプレートによる受け取り // 関数のシグネチャなどの情報がなく、追加の制約が必要になる // 可読性が低下し、複雑さが増大する template<typeanme F> void f(F&& f); // std::functionによる受け取り // 所有権が不明(`std::reference_wrapper`を保持しているかもしれない)、空かもしれない // 型消去のための追加のオーバーヘッド(動的確保など)が必要となる void f(std::function<bool(int)> f); // function_refによる受け取り void f(function_ref<bool(int)> f);
function_ref
を使用することによって、ステートフルなラムダを含めたあらゆるCallableを受け取れるようにしながら、求められている関数の情報が最小の形で引数型に表示され、追加のオーバーヘッドを回避できます。そして、function_ref
はview
であり常に所有権を持ちません。
function_ref
は次のように実装されます。
template<typename F> class function_ref; template<typename R, typename... Args> class function_ref<R(Args...)> { void* erased_object; R(*erased_function)(Args...); public: template<typename F> function_ref(F&&); // ... };
function_ref
はメンバとして2つのポインタを持ち、erased_object
は渡された関数のオブジェクトそのもの(ファンクタなど)のポインタを、erased_function
はその関数呼び出し演算子のポインタを保持します。
function_ref
はファンクタ/ステートフルラムダ、関数ポインタ/ステートレスラムダ、メンバ関数ポインタ、の3種類のCallableを保持することになりますが、2つのメンバがフルに活用されるのはファンクタ/ステートフルラムダの時だけで、それ以外のケースではerased_object
は有効利用されていません。メンバ関数ポインタに対するthis
やフリー関数の最初の引数などを束縛した上でfunction_ref
に渡すにはラムダ式などでラップする必要がありますが、erased_object
を有効利用すればその必要なく一部の引数を型消去しながら束縛することができそうです。
このような単一関数に対する実行時ポリモルフィズムは、C#のデリゲートなどOOPの世界でよく見られ、C++においてもコミュニティから類似のもののアイデアがいくつか提示されています。それらは共通して、function_ref
の2つのメンバを直接指定して初期化するコンストラクタによって実装可能です。
この提案はその実現のため、function_ref
にその2つのポインタメンバを直接初期化可能なコンストラクタを追加した上で、その初期化をサポートするmake_function_ref()
ヘルパ関数を追加するものです。
template<typename F> class function_ref; template<typename R, typename... Args> class function_ref<R(Args...)> { void* erased_object; R(*erased_function)(void*, Args...); public: template<typename F> function_ref(F&&); // 追加するコンストラクタ function_ref(void* obj, R(*func)(void*, Args...)) : erased_object{obj} , erased_function{func} {} // ... }; template<auto mf, typename T> requires std::is_member_function_pointer<decltype(mf)>::value auto make_function_ref(const T& obj) { return function_ref(std::addressof(obj), mf); } template<auto mf> requires std::is_member_function_pointer<decltype(mf)>::value auto make_function_ref() { return function_ref(mf); } template<auto f> requires is_function_pointer<decltype(f)>::value auto make_function_ref() { return function_ref(f); } // 他多数のオーバーロード
make_function_ref
はNTTPとして(メンバ)関数ポインタを、関数引数で束縛対象のオブジェクトを受け取り、それらを指定してfunction_ref
の2つのメンバを初期化します。例えば次のように利用します
struct bar { void baz(bool b, int i, float f) { std::cout << "bar::baz" << std::endl; } void baznoe(bool b, int i, float f) noexcept { std::cout << "bar::baznoe" << std::endl; } }; void third_party_lib_function1(tl::function_ref<void(bool, int, float)> callback) { callback(true, 11, 3.1459f); } void application_function_ref(function_ref<void(bar*, bool, int, float)> callback) { bar b; callback(b, true, 11, 3.1459f); } void free_baz1(bool b, int i, float f) { std::cout << "free_baz1" << std::endl; } int main() { bar b; // ステートフルラムダ(make_function_refがない場合の渡し方) third_party_lib_function1([&b](bool b1, int i, float f){ b.baz(b1, i, f); }); // メンバ関数ポインタとthis(上記と同等な渡し方) third_party_lib_function1(make_function_ref<&bar::baz>(b)); // 関数ポインタのみ third_party_lib_function1(make_function_ref<free_baz1>()); // メンバ関数ポインタのみ(thisは後から渡す) application_function_ref(make_function_ref<&bar::baz>()); application_function_ref(make_function_ref<&bar::baznoe>()); }
P2473R0 Distributing C++ Module Libraries
C++モジュールのビルド済み成果物の配布を容易にするための、ビルドツール・コンパイラ・静的解析ツール間の相互運用性のための共通フォーマットの提案。
この提案はP2409R0をベースとして、そこで挙げられている要件に沿うような規則を定めようとするものです。
パッケージマネージャを使用してビルド済みのライブラリを配布する環境では、ライブラリのコンテンツはディスク上のファイルとして表されているはずです。この提案はC++モジュールをそれらのビルド済みファイルとして配布できるようにするための規則を定めることを目的としています。
この提案の目標は以下の3点で、それ以外の点は目的としていません
- モジュールの発見可能性
- ビルドシステムとパッケージマネージャ間で決定されたモジュール検索パスが与えられた時、それを用いてシステムで利用可能なモジュールを見つけるための規則を定める
- モジュールインターフェースのパース手順
- 依存関係の検出を最適化する規則
- モジュールの依存関係グラフ構築の際に、プリプロセッサの実行を回避することは望ましい最適化の一つであるが必須ではない。この提案は、その選択をした実装者のための規約を定める
提案している規則は以下のようなものです。
モジュール名はファイル名と対応する
import
宣言で使用されるモジュール名は次の規則によってファイル名に変換される。
.
は階層を表し、ディレクトリ構造へ変換される- モジュールパーテイションは、モジュール名に
.part
サフィックスが付いたディレクトリに移動し、ファイル名はパーティション名となる - モジュールインターフェース単位の拡張子は
.ixx
となる
モジュール名 | 検索ルートからの相対パス |
---|---|
foo | foo.ixx |
foo.bar | foo/bar.ixx |
foo.bar:baz | foo/bar.part/baz.ixx |
これは、モジュールのインストールプロセスがユーザーに変わって翻訳することを想定している。これにより、ビルドシステムの外部からモジュールを消費(import
に対応した読み込み)する際に決定論的な探索が可能となる。
モジュールを消費する手順
モジュールを消費する手順はモジュールインターフェースファイルとともに(同じディレクトリに)配置され、拡張子は.meta-ixx-info
。
このファイルは次のキーを持つJSONオブジェクトとしてエンコードされる
include_path
: インターフェース単位ファイルのプリプロセス時に必要な#include
対象ファイルのリスト。標準ヘッダ類は含まれない。definitions
: 事前定義マクロのキーと値のペア。imports
: 対応するモジュールインターフェース単位によってインポートされているモジュール名のリスト。これは、ビルドシステムがモジュール単位を解析して依存関係を把握するのを回避するためのオプションのフィールド。_VENDOR_extension
: ベンダーはビルドシステムで使用される可能性のあるメタデータの拡張機能を指定するためにこのフィールドを使用できる
この規則の前提として、プリプロセス時に適用されるオプション以外のオプションは、モジュールを消費する翻訳単位とモジュールインターフェース単位のパースの間で一律に適用されなければならない。それは例えば、言語バージョンを指定するフラグやABI互換に影響するフラグなど。
また、モジュールを消費する翻訳単位からのインクルードパスとマクロ定義はインポートされるモジュールを消費する際に使用しないことも想定している。
バイナリモジュールインターフェースファイルの配布
バイナリモジュールインターフェース(BMI)の相互運用可能な範囲は非常に限られているが、コードがほぼ同じコンパイラによって生成される環境では重要な最適化となる。例えば、ほとんどのGNU/Linuxディストリビューションが該当する。ただし、BMIファイルがあるコンパイラに適用可能かどうかが識別可能である必要がある。したがって、このルールではコンパイラはサポートする互換性と同じくらい一意の識別子を提供する必要がある。
その識別子は、.bmi.vendor.id
のようなパターンで用意する。例えば、g++がUUID-v4を使用してBMIファイルを再利用可能であることを表明するには、foo.bar
モジュールの場合ファイル名は「foo/bar.bmi.g++.20734238-4fc7-4725-bf22-be9700326774」になる。
この規則では、このような識別子のうち、コンパイラは1つの入力形式だけをサポートすることを想定している。コンパイラはファイル名の有効な拡張子となる任意の識別子を使用でき、複数の識別子パターンをサポート可能だが、ここでそれを規定すると探索の複雑さが増大する。
検索順序
モジュールの探索時は、最初にコンパイラ固有のBMI(標準ライブラリのものなど)を探索してから2回目の探索で.ixx
ファイルと.meta-ixx-info
ファイルを探索できるはず。これにより、異なるビルドシステム間で使用するためにBMIファイルをキャッシュするローカルオーバーレイツールを作成できる。
BMIファイルの存在を最適化しない
ビルドシステムが現在のビルドと互換性のあるBMIファイルの存在を確認した時、依存関係の探索をやめて直接それを消費することが有効な最適化だが、そうすると、ビルドシステムは連携するツールからモジュールのインターフェースのパースを再現する方法の詳細を隠蔽してしまう。したがって、相互運用性を維持したいビルドシステムはモジュールインターフェースをパースする必要がないと判断した場合でもモジュールインターフェースのパースを継続して、その情報を現在のコンパイルデータベースのように利用する必要がある。
Discovery tooling
ライブラリとして提供されるモジュールの推移的な関係を解決するために、全ての必要なモジュールを解析するものが必要となる。そのために、c++-modules-config
と呼ばれるツールを提案する。
このツールはモジュールの探索パス、BMIファイルの識別子、解決が必要なモジュール名のリストを入力とし、ビルドに関係するモジュールの完全なリスト、インターフェースのソース、インターフェースをパースするのに必要な手順、およびモジュールの依存関係を出力する。このツールは再帰的な探索を行い、ビルドシステムが消費する必要のあるシステムによって提供される全てのモジュールのパースを計画するのに十分な記述を返す。これにより、利用可能だが消費される予定のないモジュールは除外される。
このツールの出力はP1689R4のフォーマットと互換性を持たせる必要があり、またこの出力はmodule_config.json
という名前で、現在compile_commands.json
が保存されている場所と同じ場所に保存される必要がある。
P2475R0 WG21 2021-10 Virtual Meeting Record of Discussion
2021年10月4日(北米時間)に行われた、WG21全体会議のデイスカッション議事録。
CWG/LWG/LEWGの投票時のディスカッションの様子が記録されています。
P2477R0 Allow programmer to control and detect coroutine elision by static constexpr bool should_elide() and
コルーチンの動的メモリ確保を避ける最適化を制御し、起こったことを検出するAPIを追加する提案。
コルーチンはサスペンドと再開をサポートするためにその状態(コルーチンステータス)を保存しておく領域を必要とします。現在のC++コルーチンでは、この領域の確保は動的メモリ確保(operator new
)によって行われ、それはコストがかかります。
それを軽減するために、HALOと呼ばれる動的メモリ確保を回避する最適化がデザインされており、これをcoroutine elisionと呼びます。clang/LLVMではそれはCoroElideとして実装されています。C++標準では名前付けされていないもののcoroutine elisionは許可されています(つまり必須ではない)。
これはコンパイラの最適化であり、必ず行われるわけではありません。そのため、コルーチンを利用するプログラマはこの最適化を制御することも、それが行われたことを察知することもできません。
また、組み込みのようにリソースが限られている環境ではcoroutine elisionを無効化したうえでpromise_type::operator new
が静的ストレージのバイト配列からの領域を返すようにすることが好まれることがあるようです。しかし、coroutine elisionを無効化する方法もまた標準では提供されていません。
この提案では、coroutine elisionを制御するためのpromise_type::should_elide()
、coroutine elisionを検出するためのstd::coroutine_handle<>::elided()
の2つの関数を追加することを提案しています。これによって、プログラマはcoroutine elisionを制御し検出できるようになります。
should_elide()
は次のようにcoroutine elisionの実行についてをコンパイラに指示します
promise_type
のスコープでshould_elide()
という関数を見つけることができ、コンパイル時に評価した結果がtrue
に評価された場合 : coroutine elisionが保証されるfalse
に評価された場合 : coroutine elisionが行われないことが保証される
should_elide()
が見つからない、コンパイル時に評価できない場合- 従来通り(coroutine elisionはコンパイラの裁量で行われる)
should_elide()
はpromise_type
のメンバ関数として実装されるもので、promise_type
はコルーチンの動作をカスタムするカスタマイゼーションポイントとしてユーザーが定義します。したがって、should_elide()
もまた必要であればユーザーが定義するものであり、それはconstexpr
関数として定義しなければなりません。
elided()
はstd::coroutine_handle
のメンバ関数として追加され、coroutine elisionが行われていた時にtrue
を返し、行われていない場合にfalse
を返します。
namespace std { template<> struct coroutine_handle<void> { // ... bool elided() const noexcept; // 追加 // ... }; template<class Promise> struct coroutine_handle<Promise> { // ... bool elided() const noexcept; // 追加 // ... }; }
P2478R0 _Thread_local for better C++ interoperability with C
C++thread_local
変数は動的初期化とデストラクタ呼び出しを伴うことができます。その初期化は、スレッド起動時に非ブロック(名前空間スコープ)変数に対して行われるか、最初に変数が使用されるときに行われる可能性があります。その主な実装は、変数が使用される場所に関数を挿入し、その関数が呼ばれることで非ブロック変数の遅延初期化を実行します。
そのような関数とその呼び出しによって実行時にサイズと時間のオーバーヘッドがかかります。これは変数が動的初期化もデストラクタ呼び出しも必要ないことが分かっていれば回避可能です。
このことは、単にオーバーヘッドがかかるだけではなく、Cの_Thread_local
との非互換を生み出しています。Cの_Thread_local
はC++thread_local
としての使用に必要なもの(初期化・破棄に必要な追加の関数など)を提供しません。このため、C_Thread_local
変数をC++コードから参照しようとするとリンクエラーを起こすことがあります。
また、C++で定義された動的初期化されるか非トリビアルなデストラクタをもつ静的変数(非スレッドローカル)にCからアクセスすることは、C++から同じものにアクセスするのと比べて危険ではありません。なぜなら、主要な実装はプログラム起動時にそれらの初期化を行うためです。しかし、同様に宣言されているスレッドローカルな変数にアクセスした場合は同じことは言えません。Cからのアクセスは初期化前の変数を観測する可能性が高くなります。
この提案はC23に対して、_Thread_local
をthread_local
キーワードで置き換えるのをやめるように提案するものです。上記のような問題があるため、安易にキーワードをC++と共通化してしまうことは危険です。一方、C++が上記問題の回避を行いたい場合にCと同様のセマンティクスで_Thread_local
を使用する方向性を示しています。
P2479R0 Slides for P2464
P2464R0 Ruminations on networking and executorsの紹介スライド。
後半ではP2469に対する反論も行われています。
P2480R0 Response to P2471: "NetTS, Asio, and Sender library design comparison" - corrected and expanded
P2471R1の訂正や追記を行う文書。
P2471R1はASIO/NetrworkingTSとP2300との比較を行っている文書ですが、その内容について不正確な部分を修正したり追記したりする文書です。
P2481R0 Forwarding reference to specific type/template
テンプレートパラメータ指定時にconst
と値カテゴリを推論可能にする構文の必要性について説明した文書。
std::tuple<Ts...>
にはstd::tuple<Us...>
からの変換コンストラクタが存在します。それは、Us...
のそれぞれが対応するTs...
のそれぞれに変換可能であれば利用可能となりますが、std::tuple<Us...>
の状態によって4つの定義を必要とします。
template <typename... Ts> struct tuple { template <typename... Us> requires sizeof...(Ts) == sizeof...(Us) && (is_constructible_v<Ts, Us&> && ...) tuple(tuple<Us...>&); template <typename... Us> requires sizeof...(Ts) == sizeof...(Us) && (is_constructible_v<Ts, Us const&> && ...) tuple(tuple<Us...> const&); // (1) template <typename... Us> requires sizeof...(Ts) == sizeof...(Us) && (is_constructible_v<Ts, Us&&> && ...) tuple(tuple<Us...>&&); // (2) template <typename... Us> requires sizeof...(Ts) == sizeof...(Us) && (is_constructible_v<Ts, Us const&&> && ...) tuple(tuple<Us...> const&&); };
これは簡易な例でしかないので正確ではありません。重要なことは、tuple<Us...>
がconst
の有無と値カテゴリの組み合わせで2x2=4つのコンストラクタオーバーロードが必要となるうえ、制約もそれに応じて微妙に細部を調整しなければなりません。
これはかなり冗長なコードであるうえ、これらがオーバーロードであることから、ある適切なコンストラクタがその制約によって適切に候補から外れた時でも、別のコンストラクタが呼ばれることがあります。
void f(tuple<int&>); auto g() -> tuple<int&&>; void h() { f(g()); // ok! }
これは、tuple<int&&> -> tuple<int&>
の構築を行っているもので、int&& -> int&
のような変換は不可能なので失敗してほしいし失敗するはずです。しかし実際には、(2)のコンストラクタが制約を満たさないために除外された後、(1)のコンストラクタが利用されます。g()
の戻り値は右辺値であり、(1)のコンストラクタのtuple<Us...> const&
でも受けることができて、その制約中のUs const&
はint&& const & -> int&
となってint& -> int&
の変換が可能であることから(1)のコンストラクタを呼び出してしまいます。
この問題を回避するためには、コンストラクタテンプレートを1つだけにする必要があります。必要なことは、tuple<Us...>
を指定しながら、CV・参照修飾を推論してもらうことです。現在のところそれはできず、上記のように書くかテンプレートをこね回す必要があります。
template <typename... Ts> struct tuple { template <typename Other> requires is_specialization_of<remove_cvref_t<Other>, tuple> && /* ???? */ tuple(Other&& rhs); };
しかし、このコンストラクタテンプレートの制約を正しく書く事は困難で、いい解決策はありません。結局、最低4つの似たようなオーバーロードを用意するのが一番簡易となります。
これと同じ問題は、std::get
でも見ることができます。
template <size_t I, typename... Ts> auto get(tuple<Ts...>&) -> tuple_element_t<I, tuple<Ts...>>&; template <size_t I, typename... Ts> auto get(tuple<Ts...> const&) -> tuple_element_t<I, tuple<Ts...>> const&; template <size_t I, typename... Ts> auto get(tuple<Ts...>&&) -> tuple_element_t<I, tuple<Ts...>>&&; template <size_t I, typename... Ts> auto get(tuple<Ts...> const&&) -> tuple_element_t<I, tuple<Ts...>> const&&;
これも一つの関数テンプレートにまとめることで記述を削減でき先ほどのような問題を回避できますが、やはりその制約を正しく書くことが困難で、std:tuple
の派生型に対して機能しなくなってしまいます。
std::optional::transform
のようなメンバ関数とdeducing thisを組み合わせた時にも、別の問題が浮かび上がります。
template <typename T> struct optional { template <typename F> constexpr auto transform(F&&) &; template <typename F> constexpr auto transform(F&&) const&; template <typename F> constexpr auto transform(F&&) &&; template <typename F> constexpr auto transform(F&&) const&&; };
このような冗長なコードは、deducing thisによって次のようにひとまとめにできます。
template <typename T> struct optional { template <typename Self, typename F> constexpr auto transform(this Self&&, F&&); };
しかし、今度はこのSelf
の対象とする範囲が広すぎます。ここではstd::optional
の派生型を対象としたくはなく、やってほしいことはCV・参照修飾の推論だけです。
これらの問題は、テンプレートにおいてある程度型を指定しながらでもCV・参照修飾の推論が可能となってくれれば解決することができます。この文書では、いくつかの構文の候補を提示するとともにより良いアイデアを募っています(まだ提案には至っていません)。
以下の例では次のようなユーティリティを使用しています
#define FWD(e) static_cast<decltype(e)&&>(e) template <bool RV, typename T> using apply_ref = std::conditional_t<RV, T&&, T&>; template <bool C, typename T> using apply_const = std::conditional_t<C, T const, T>; template <bool C, bool RV, typename T> using apply_const_ref = apply_ref<RV, apply_const<C, T>>; template <typename T, typename U> using copy_cvref_t = apply_const_ref< is_const_v<remove_reference_t<T>>, !is_lvalue_reference_v<T>, U>;
T auto&&
これはコンセプトによるプレースホルダ型の制約(range auto&& r
)を発展させて、コンセプトを指定する部分により具体的な型を指定できるようにしたものです
template <typename... Ts> struct tuple { template <typename... Us> tuple(tuple<Us...> auto&& rhs) requires sizeof...(Us) == sizeof...(Ts) && (constructible_from< Ts, copy_cvref_t<decltype(rhs), Us> > && ...); };
template <typename T> struct optional { template <typename F> auto transform(this optional auto&& self, F&& f) { using U = remove_cv_t<invoke_result_t<F, decltype(FWD(self).value())>; if (self) { return optional<U>(invoke(FWD(f), FWD(self).value())); } else { return optional<U>(); } } };
- 利点
- 簡潔にやりたいことを実現・表現できる
- 欠点
- 正確な型を取得するには
decltype(param)
としなければならない- このため、
requires
節を関数の後ろにしか書けなくなる
- このため、
concept auto
の記法と矛盾する(この構文は変換を行わない)
- 正確な型を取得するには
T&&&
先ほどの、T auto&&
の代わりにT&&&
を導入するものです。(サンプルは省略)
- 利点
tuple<Us...>&&&
は派生型からの変換を行うことができる
- 欠点
T auto&&
と同じT&&&
という参照トークンそのもの
const(bool)
noexcept(bool)
やexplicit(bool)
のように、const
と&&
を条件付きにするものです。
template <typename... Ts> struct tuple { template <typename... Us, bool C, bool RV> requires sizeof...(Us) == sizeof...(Ts) && (constructible_from< Ts, apply_const_ref<C, RV, Us> > && ...) tuple(tuple<Us...> const(C) &&(RV) rhs); };
template <typename T> struct optional { template <typename F, bool C, bool RV> auto transform(this optional const(C) &&(RV) self, F&& f) { using U = remove_cv_t<invoke_result_t<F, // apply_const_ref<C, RV, T> decltype(FWD(self).value())>; if (self) { return optional<U>(invoke(FWD(f), FWD(self).value())); } else { return optional<U>(); } } };
- 利点
const
性と参照修飾だけを指定していることが明確になるrequires
節の配置自由度が保たれる
- 欠点
- 構文が奇妙(特に
&&(bool)
) - 推論された
bool
値を使用するのにメタプログラミングが必要になる
- 構文が奇妙(特に
Q
修飾子
これは全く新しい形のテンプレートパラメータを導入するもので、これをqualifier
(修飾子)と呼びます。これは、エイリアステンプレートを推論します
template <typename... Ts> struct tuple { template <typename... Us, qualifiers Q> requires sizeof...(Us) == sizeof...(Ts) && (constructible_from<Ts, Q<Us>> && ...) tuple(Q<tuple<Us...>> rhs); };
template <typename T> struct optional { template <typename F, qualifiers Q> auto transform(this Q<optional> self, F&& f) { using U = remove_cv_t<invoke_result_t<F, Q<T>>>; if (self) { return optional<U>(invoke(FWD(f), FWD(self).value())); } else { return optional<U>(); } } };
ここでは、Q<T>
のQ
とT
はそれぞれ別々に推論されますが、Q
は次のエイリアステンプレートのいずれかとして推論されます
template <typename T> using Q = T&; template <typename T> using Q = T const&; template <typename T> using Q = T&&; template <typename T> using Q = T const&&;
- 利点
- 推論したCV・参照修飾を適用(利用)するのが容易
- 正確な型の取得が容易
- 欠点
- 斬新な構文であり、奇妙
Q
の名前が問題となるconst
だけを推論したいときに、誤用の可能性がある
作者の方はこれらの解決策に納得してないらしく、より良い構文を募るためにこの文書を書いたようです。