文書の一覧
全部で113本あります。
もくじ
- N5011 Brno 2026
- N5029 WG21 2025-10 Kona Admin telecon minutes
- N5031 WG21 2025-11 Kona Minutes of Meeting
- N5032 Working Draft, Standard for Programming Language C++
- N5033 Editors' Report - Programming Languages - C++
- P1317R2 Remove return type deduction in std::apply
- P1789R2 Library Support for Expansion Statements
- P1789R3 Library Support for Expansion Statements
- P2243R0 Language linkage for templates
- P2728R9 Unicode in the Library, Part 1: UTF Transcoding
- P2728R10 Unicode in the Library, Part 1: UTF Transcoding
- P3064R3 How to Avoid OOTA Without Really Trying (Informational)
- P3097R1 Contracts for C++: Virtual functions
- P3099R1 Contracts for C++: User-defined diagnostic messages
- P3100R5 Implicit contract assertions
- P3216R2 views::slice
- P3220R2 views::take_before
- P3371R5 Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS
- P3388R3 When Do You Know connect Doesn't Throw?
- P3391R2 constexpr std::format
- P3395R5 Fix encoding issues and add a formatter for std::error_code
- P3400R2 Controlling Contract-Assertion Properties
- P3412R3 String Interpolation
- P3424R1 Define Delete With Throwing Exception Specification
- P3505R2 Fix the default floating-point representation in std::format
- P3505R3 Fix the default floating-point representation in std::format
- P3612R1 Harmonize proxy-reference operations (LWG 3638 and 4187)
- P3642R3 Carry-less product: std::clmul
- P3657R1 A Grammar for Whitespace Characters
- P3657R2 A Grammar for Whitespace Characters
- P3666R2 Bit-precise integers
- P3684R1 Fix erroneous behaviour termination semantics for C++26
- P3688R5 ASCII character utilities
- P3692R3 How to Avoid OOTA Without Really Trying
- P3695R3 Deprecate implicit conversions between char8_t and char16_t or char32_t
- P3724R2 Integer division
- P3726R1 Adjustments to Union Lifetime Rules
- P3735R1 partial_sort_at_most, nth_element_at_most
- P3737R2 std::array is a wrapper for an array!
- P3739R4 Standard Library Hardening - using std::optional<T&>
- P3744R0 Explicit Provenance APIs
- P3751R0 A gentle introduction to pointer authentication
- P3751R1 A gentle introduction to pointer authentication
- P3763R1 Remove redundant reserve_hint members from view classes
- P3772R1 std::simd overloads for bit permutations
- P3793R1 Better shifting
- P3804R1 Iterating on parallel_scheduler
- P3815R1 Add scope_association concept to P3149
- P3816R1 Hashing meta::info
- P3824R2 Static storage for braced initializers NBC examples
- P3826R1 Fix or Remove Sender Algorithm Customization
- P3826R2 Fix or Remove Sender Algorithm Customization
- P3833R0 std::multi_lock
- P3834R2 Defaulting the Compound Assignment Operators
- P3836R2 Make optional<T&> trivially copyable (NB comment US 134-215)
- P3843R1 Reconsider R0 of P3774 (Rename std::nontype) for C++26
- P3843R2 std::function_wrapper
- P3844R1 Restore simd::vec broadcast from int
- P3844R2 Restore simd::vec broadcast from int
- P3847R0 Lambdas capture left to right
- P3849R1 SIS/TK611 considerations on Contract Assertions
- P3852R0 a (constexpr) utility to check if pointer points between two related pointers
- P3856R1 New reflection metafunctions - is_structural_type (US NB comment 49) and is_destructurable_type
- P3856R2 New reflection metafunctions - is_structural_type (US NB comment 49) and is_destructurable_type
- P3856R3 New reflection metafunctions - is_structural_type (US NB comment 49) and is_destructurable_type
- P3858R1 A Lifetime-Management Primitive for Trivially Relocatable Types
- P3860R1 Proposed Resolution for NB Comment GB13-309 atomic_ref is not convertible to atomic_ref
- P3868R1 Allow #line before module declarations
- P3869R0 Slides for P3666R1 Bit-precise integers
- P3869R1 Slides for P3666R1 Bit-precise integers
- P3876R0 Extending <charconv> support to more character types
- P3878R0 C++26 Contracts are not a good fit for standard library hardening
- P3878R1 Standard library hardening should not use the 'observe' semantic
- P3880R0 Make subspan aware of compile-time constants
- P3881R0 Forward-progress for all infinite loops
- P3883R0 A Proposal for a Boolean Flip Operator in C++
- P3884R0 Slides for P3505R2 Fix the default floating-point representation in std::format
- P3885R0 Add a formatter for std::error_category
- P3886R0 Wording for AT1-057
- P3887R0 Make when_all a Ronseal Algorithm
- P3887R1 Make when_all a Ronseal Algorithm
- P3889R0 A minimal solution for contracts, or, what is an MVP?
- P3890R0 Add description for parallel memory algorithms
- P3891R0 Improve readability of the C++ grammar by adding a syntax for groups and repetitions
- P3892R0 unless_stop_requested
- P3893R0 The CppCon 2025 Talk on Contracts and CodeQL in Context
- P3895R0 Slides for P3724R1 - Integer division
- P3896R0 Design goals for a contract support facility
- P3897R0 Slides for P3776R1 - More trailing commas
- P3898R0 Slides for P3793R0 - Better shifting
- P3899R0 Clarify the behavior of floating-point overflow
- P3902R0 Against implicit conversions for indirect
- P3902R1 Against implicit conversions for indirect
- P3902R2 Against implicit conversions for indirect
- P3904R0 When paths go WTF: making formatting lossless
- P3905R0 C++ Standard Library Ready Issues to be moved in Kona, Nov. 2025
- P3906R0 C++ Standard Library Immediate Issues to be moved in Kona, Nov. 2025
- P3907R0 Waving more ::result_type goodbye
- P3908R0 constexpr from_chars<float> / to_chars<float>
- P3909R0 Contracts should go into a White Paper - even at this late point
- P3910R0 Improving safety of C++26 contracts
- P3911R0 RO 2-056 6.11.2 [basic.contract.eval] Make Contracts Reliably Non-Ignorable
- P3912R0 Design considerations for always-enforced contract assertions
- P3913R0 Optimize for std::optional in range adaptors
- P3913R1 Optimize for std::optional in range adaptors
- P3914R0 Assorted NB comment resolutions for Kona 2025
- P3915R0 Responses to Trivial Relocation NB Comments
- P3917R0 A Lifetime-Management Primitive for Trivially Relocatable Types (Presentation)
- P3919R0 Guaranteed-(quick-)enforced contracts
- P3920R0 Wording for NB comment resolution on trivial relocation
- P3921R0 Core Language Working Group "ready" Issues for the November, 2025 meeting
- P3922R0 Missing deduction guide from simd::mask to simd::vec
- P3922R1 Missing deduction guide from simd::mask to simd::vec
- P3923R0 Additional NB comment resolutions for Kona 2025
- P3924R0 Fix inappropriate font choices for "declaration"
- P3925R0 RO 3-292 29.10.8.3 [simd.comparison] Make basic_simd a Regular Type (with Boolean operator==)
- P3926R0 Slides: operator T& on indirect<T> (in defense of US 77-140)
- P3928R0 static_sized_range
- P3929R0 Fix safety hazard in std::function_ref
- P3931R0 consteval all the non-allocating operator"" things
- P3933R0 constexpr std::hive
- P3935R0 Rebasing <cmath> on C23
- P3936R0 Safer atomic_ref::address (FR-030-310)
- P3937R0 Type Erasure Requirements For Future Trivial Relocation Design
- P3938R0 Values of floating-point types
- P3940R0 Rename concept tags for C++26: sender_t to sender_tag
- P3941R0 Scheduler Affinity
- P3945R0 Comments on D3933R0 (constexpr hive)
- P3946R0 Designing enforced assertions
- P3947R0 identifier_of Should Return std::string
- P3948R0 constant_wrapper is the only tool needed for passing constant expressions
- おわり
2026年06月に予定されている全体会議の案内。
2026/06/08~2026/06/13にチェコのBrnoで開催される予定です。
文書では会場や行き方などの案内がされています。
2025年10月26日に行われた、WG21管理者ミーティングの議事録。
前回からどのような活動があったかや、Kona会議で何をするかなどの報告がなされています。
2025年11月にKonaで行われた全体会議の議事録。
最終日に行われた全体会議での各グループの作業報告と、全体投票の様子が記録されています。
C++26のワーキングドラフト第9弾。
↑の変更点をまとめた文書。
std::applyの戻り値型推論をやめる提案。
以前の記事を参照
このリビジョンでの変更はよくわかりませんが、文言の変更のみのようです。特に、R1で提案していた関連するコンセプトが削除されています。
この提案は2025年6月の全体会議でC++26に向けて採択済みです。公開を忘れていた様子です。
↓
std::integer_sequenceを構造化束縛および展開ステートメントで使用できるようにする提案。
以前の記事を参照
R2での変更は
- 関連する機能テストマクロを更新
- NBコメント対応
- NCIT-002
- FR 007-011-142
- CZ 2-143
これらのNBコメントはいずれも、この提案をC++26に入れることを推奨するものです。
このリビジョンでの変更は
- LWGレビューを反映
- 不要なコメントの削除
const integer_sequenceの特殊化を追加
などです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
テンプレートに対するCリンケージ指定を許可する提案。
言語リンケージの指定とはextern "C"のような指定を宣言に付加したり、あるいはブロックに付加したりすることを言います。これはエイリアス宣言や関数宣言、変数宣言などに対して行うことができますが、テンプレートに対する言語リンケージの指定は明確に禁止されています。
ただし、現在の規定ではこの「言語リンケージ(language linkage)」という言葉の指すものが非常にあいまいであり、特にテンプレートに対する言語リンケージという言葉がほとんど定義されていないという問題があります。
関数に対する言語リンケージとは、ABIにおける呼び出し規約を反映したものになっています。
void f();
extern "C" typedef void cf();
cf *p = f;
(ただし一部の実装ではこれを受け入れるようです)
すなわち、関数に対する言語リンケージの指定は、その関数の呼び出し規約を指定することに対応しています。
一方、変数に対する言語リンケージは、ABIにおける名前マングルを反映したものになっています。
namespace N {
typedef void* mk(int);
extern "C" mk make_vector;
void* make_vector(int i)
{
return new std::vector<int>(i);
}
}
int make_vector;
グローバル名前空間スコープの変数に対しては名前マングルが行われないため、この衝突が起きています。
すなわち、変数に対する言語リンケージの指定は、その変数名の名前マングル方法を指定(C++マングルの抑制)することに対応しています。
ちなみに、グローバル名前空間スコープの変数名に対するこの規則はなぜか演算子関数に対しても同様に適用されます。
struct A {};
struct B {};
extern "C" {
void operator+(A);
void operator+(B);
}
一方、ユーザー定義リテラルではこのようなC言語リンケージの指定が禁止されています。
演算子関数はCリンケージで呼び出されることはないので、本来は不要なはずです(このことは言語リンケージというものの曖昧さを表しています)。
そして、テンプレート(関数・変数・クラス・コンセプトを含む)は一律に言語リンケージの指定が禁止されています。ここでの言語リンケージが何を指す(上記のいずれの意味なのか、また別の意味なのか)のかは特に規定されていません。
テンプレートは特殊化ごとに異なる名前を持つことになる(独自の名前マングル方法を持つ)ため名前マングルの指定は不要です。一方、呼び出し規約の指定の側面は有用である可能性があります。しかし現在はその区別なく禁止されているため、関数テンプレートや関数型を示すエイリアステンプレートに対して呼び出し規約を指定することができなくなっています。
例えば次のように、いくつかの種類のオブジェクトを受け取るC APIがあるとき
#ifndef GETTERS_H
#define GETTERS_H
#ifdef __cplusplus
extern "C" {
#endif
struct things {unsigned a,b;};
void get_int(int*);
void get_int_fast(int*);
void get_float(float*);
void get_things(struct things*);
#ifdef __cplusplus
}
#endif
#endif
これを次のようにラップして使いたくなることもあるでしょう
#include"getters.h"
namespace wrap {
extern "C" typedef void (*int_getter)(int*);
int get_int(int_getter f) {
int ret;
f(&ret);
return ret;
}
}
namespace client {
void g() {
auto val = wrap::get_int(get_int_fast);
}
}
これを一歩進めてテンプレート化したくなるかもしれません
namespace wrap {
template<class T>
T get( f) {
T ret;
f(&ret);
return ret;
}
}
このとき、このget<T>の引数型(fの型)を正しく記述することが非常に困難になります。なぜなら、void (*f)(T)と書くとこれはC++リンケージを持つ関数ポインタ型となってしまいC言語リンケージを持つ関数ポインタ型として記述できないためです。
テンプレートに言語リンケージを指定できないことから、エイリアステンプレートとして記述することもできません。
namespace wrap {
extern "C"
template<class T>
using getter=void(*)(T);
template<class T>
T get(getter<T> f) {
T ret;
f(&ret);
return ret;
}
}
このように、関数テンプレートおよびエイリアステンプレートに対して呼び出し規約を指定するためにC言語リンケージを指定することには価値がある可能性があります。
別の例として、次のようにオブジェクトの破棄の責任を受け取るC APIを考えます
#ifndef CLEANUP_H
#define CLEANUP_H
#ifdef __cplusplus
extern "C" {
#endif
void transfer_ownership(void*,void(void*));
#ifdef __cplusplus
}
#endif
#endif
これもまたラップしてデストラクタで使用できるようにしたくなることもあるでしょう
#include "cleanup.h"
#include <memory>
class A {};
extern "C" void del_A(void *p) {
delete static_cast<A*>(p);
}
void transfer_A(std::unique_ptr<A> a) {
::transfer_ownership(a.release(), del_A);
}
これを一歩進めてテンプレート化しようとすると、同様にC言語リンケージを指定できない問題にぶつかります
extern "C" template<class T>
void del(void *p) {
delete static_cast<T*>(p);
}
このように、関数テンプレート自体にもC言語リンケージを指定できると有用である可能性があります。
この提案は、現在のテンプレートに対する言語リンケージの禁止という制限を削除し、テンプレートに対するCリンケージの指定を許可しようとするものです。この提案の後では、上記の2例はそのまま許可されるようになります。
この提案は次のコードを許可するものではなく
template<class>
extern "C++" void f() {}
次のコードを許可するものです
extern "C" {
template<class T>
void f(T);
template<class T>
void g(void(T));
}
template<class T>
void f(T) {}
template<class T>
void g(void(T)) {}
このコードでは、関数テンプレートfを1つ宣言(定義)し、fの特殊化の型はCリンケージを持ちます。一方、関数テンプレートgは2つ宣言されており、そのうちの一つはC関数へのポインタを受け入れ、Cの呼び出し規約を使用します。
この提案はC++29をターゲットにしていますが、EWGではDRとすることで合意が取れているようです。
↓
以前の記事を参照
R9での変更は
- コピー不可能な
input_iteratorに関する文言におけるconstバグの修正
buf_にstd::arrayではなくstd::inplace_vectorを使用することで、説明専用のbuf_last_を削除
to-utf-view-implの演算子実装を簡略化
utf-iteratorをto-utf-view-impl::iteratorにリネーム
to-utf-view-impl::sentinel型を追加
begin()をキャッシュする
reserve_hint()を追加
input_iteratorのoperator==のバグ修正
- 設計に関する議論を追加
- UTF-32との間で変換を行う場合に
size()を追加
このリビジョンでの変更は
bool OrErrorCTPをranges::subrange_kindのようなto_utf_view_error_kind列挙型に置き換え
to_utfX_viewクラスをto_utf_viewに統合し、CTADを動作させるためにコンストラクタタグを追加
_or_error CPOを使用した場合にempty_viewの値型が正しく設定されないバグを修正
- CPO templateについて、
views::adjacent_transform<N>などの前例があったため新規性があるとする記述を削除
begin()をキャッシュしない
- CPOで
charN_tの配列を拒否する
- CPOで二重変換の最適化を実装する
などです。
C++コンパイラによる実装においては、OOTA問題が発生しないことを解説する文書。
以前の記事を参照
このリビジョンでの変更は
- RFUBとNOOTABOGAのリトマステストを追加
- 情報提供文書であることを明記
- 対応する提案文書としてP3692を指定
などです。
契約プログラミング機能において、仮想関数に対する契約の指定をサポートする提案。
以前の記事を参照
このリビジョンでの変更は
- R0にあった他言語の例や代替案などの設計空間の分析に関する記述がP3600R0へ分離された
- 「Design goals and principles」セクションの追加
- P2900とこの提案(リビジョン)に関する背景を追記
- N5014へのリベース
などです。
この提案は一旦はP2900に取り込まれていたものの、EiffelやDで確立され実展開されているモデルから逸脱しており十分な導入と実装の経験が不足している、という懸念が提起され、C++26 Contracts仕様からは削除されC++29以降に向けて再検討することになりました。
再検討のためにContracts機能におけるユースケースや設計空間、およびトレードオフに関する詳細な分析を行い(P3600R0)、その分析に基づいてR0の設計がC++にとって最適なソリューションであるとして再提案しています。ただし、以前とは異なりGCCにおいてこの提案の実装が行われており、それについても言及されています。
したがって、このリビジョンではその提案内容や設計はR0から変化していません。設計根拠がより強化されており、契約アサーション機能を実装しているEiffel/D/Aidaの設計がC++にそのまま採用できるものではないことが詳しく説明されています。
契約アサーションにユーザーがエラーメッセージを指定できるようにする提案。
以前の記事を参照
このリビジョンでの変更は
- "Empty string vs. no string"セクションを追加
- メッセージが指定されていない場合に、
contract_violation::message()から何を返すか?
nullptr or 空文字列
- メッセージが指定されていない事と空文字が指定されていることを区別できるため、
nullptrを選択
などです。
UB(及びEB)を契約違反として扱うようにする提案。
以前の記事を参照
このリビジョンでの変更は
- 未定義動作リストに、見落とされていた未定義動作を1件追加
などです。
元の範囲の連続した一部分を切り出すRangeアダプタ、views::sliceの提案。
以前の記事を参照
このリビジョンでの変更は
などです。
入力の範囲を指定した値が最初に出現する位置を終端として切り出すRangeアダプタ、views::take_beforeの提案。
以前の記事を参照
このリビジョンでの変更は
- 要素型が
tidy-objを満たす(かつview型がborrowed_rangeである)場合にborrowed_rangeとなるようにする
tidy-obj: 空のクラスかつトリビアルにデフォルト構築可能かつトリビアルに破棄可能
などです。
<linalg>の一部の関数を対応するBLAS関数の仕様と整合させる提案。
以前の記事を参照
- P3371R0 Fix C++26 by making the symmetric and Hermitian rank-k and rank-2k updates consistent with the BLAS - WG21月次提案文書を眺める(2024年08月)
- P3371R1 Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS - WG21月次提案文書を眺める(2024年09月)
- P3371R2 Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS - WG21月次提案文書を眺める(2024年10月)
- P3371R3 Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS - WG21月次提案文書を眺める(2024年12月)
- P3371R4 Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS - WG21月次提案文書を眺める(2025年04月)
このリビジョンでの変更は
などです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
execution::connectによる操作が例外を送出するかどうかを早期に判定できるようにする提案。
以前の記事を参照
このリビジョンでの変更は
などです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
std::format()をconstexpr化する提案。
以前の記事を参照
このリビジョンでの変更は
std::to_stringとstd::to_wstringにconstexprを付加
などです。
std::error_codeをフォーマット可能にする提案。
以前の記事を参照
このリビジョンでの変更は
std::filesystem::pathと整合性を持つようにwchar_tの文言中の扱いを修正
- 今後の変更によるABIへの影響について明確化
- デバッグフォーマットがエラーコード全体に適用される理由を明確化
error_categoryのフォーマッタの提案へのリンクを追加
- 謝辞を追加
などです。
契約アサーションに対してラベルを指定する機能の提案。
以前の記事を参照
このリビジョンでの変更は
- introductionを再構成
- 違反ハンドラからラベルオブジェクトにアクセスする際の仕様を修正
- ベースラインの要素と今後の作業内容を分離
- 代替設計の方向性について明確化
- チェックされないセマンティクスを許容しない契約アサーションで発生する問題に対処
などです。
std::format/std::print向けの引数となるフォーマット文字列と対象引数列の組を生成する、文字列補完リテラルの提案。
以前の記事を参照
このリビジョンでの変更は
printfのサポートを削除
- stringize argumentに
fリテラルが含まれる場合の文字列化の処理方法に関する説明を追加
_Pragmaは式フィールドで使用できることを明確化
- 式フィールド内のラムダ・statement expression(GCC)・
^block(Clang)は式フィールドの終了検出に問題を起こさないことを指摘
- 生文字列リテラルは
fリテラルと特別な方法で相互作用しないことを明確化
- ただし、フェーズ2(行の連結)の反転処理は
fリテラルのうち式フィールド以外の部分で実行されなければならない
- ユーザーが記述した
__format__呼び出しが連結と文字列化に与える影響について議論
- モジュール関連の相互作用に関するセクションを追加
::がフォーマット指定子を開始できることを指摘(rangeフォーマッタの場合)
- このリビジョンではこの問題は解決されず、
::は一つのpp-tokenとして認識され、フォーマット指定子を開始しない
std::formatがconstexprになったため、__format__にもconstexprを付加
- 実装可能性に関する各ベンダからのフィードバックを追加
- 式フィールドの最上位レベルで条件演算子の使用を禁止する
- これにより、prefix演算子として
?の使用を許可する
- 式フィールドの終了判定も多少簡素化される
- R2で議論された2つの記述戦略を組み合わせた記述の最初のバージョンを追加
cpp_string_format属性を削除し、代わりにオーバーロード解決が失敗するか、暗黙的な変換シーケンスでconsteval関数を呼び出す必要がある場合には、__format__引数リストを展開するようにする
- デバッグ機能を削除
- この機能があると、式フィールドの内容を字句解析する前に、フェーズ3で先行する文字列リテラルpp-tokenを出力できなくなる
- clangでの実装は容易だったものの、フェーズ3でpp-tokenをバッファリングできないコンパイラでは実装不可能
などです。
std::printでstd::format()を経由しないためにR3で導入された[[cpp_format_string(N)]]属性はオーバーロード解決フェーズに影響を及ぼすことでそれを実現するものでした。このリビジョンではそれを省いて、単にstd::print(f"Value {1}")のように書いたときにそのままfリテラルを展開してオーバーロードに失敗する場合に、fリテラル展開後の__format__(args...)の引数args...をそこに展開するようにしています。
ただし、std::format()がコンパイル時に呼び出せる場合はコンパイル時のstd::format()で完了させます。
std::string a;
a = f"Value {0}";
std::print(f"Value {a}");
std::print(f"Wrong value {1}");
std::print("String value: {}", f"Some value {3}");
これらの使用コードは次のように展開されます
a = __format__("Value {}", 0);
std::print("Value {}", a);
std::print("Wrong value {}", 1);
std::print("String value: {}", __format__("Some value {}", 3));
delete演算子オーバーロードが例外を送出しうる例外仕様を持つことを禁止する提案。
以前の記事を参照
このリビジョンでの変更は
- 最新のWDにリベース
- 非推奨に関する記述を削除
- CWG 2024が解決されることを確認
などです。
↓
std::format()における浮動小数点数出力時のデフォルトの表現(gオプション)を修正する提案。
以前の記事を参照
R2での変更は
std::to_charsを修正するオプションを追加
__cpp_lib_to_chars, __cpp_lib_constexpr_charconv機能テストマクロを更新
このリビジョンでの変更は
- LEWG投票結果の追加
std::formatだけでなくstd::to_charsも修正されていることを明確化
- オプション1を文言から削除し、LEWGが承認したオプション2のみを残す
などです。
LEWGのレビューにおいては、この提案の内容をDR(to_chars()関連はC++17、format関連はC++20)とすることで合意が取れているようです。
vector<bool>::referenceとbitset<N>::referenceの間で一貫したIssue解決を行う提案。
以前の記事を参照
このリビジョンでの変更は編集上の修正のみです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
整数のキャリーレス乗算を行う関数の提案。
以前の記事を参照
このリビジョンでの変更は
operator<=>に不足していた<T>を追加
- §4. Possible implementation にSIMDに適した実装を追記
- 文言のリベースと改善
などです。
↓
C++規格文書における空白(whitespace)の扱いを明確化する提案。
以前の記事を参照
R1での変更は
- N5014にリベース
- NBコメントUS 5-108への対応を明確化
- 文言を大幅に再構成
- 動作を変更する可能性のある提案をすべて削除
- トークンを区切る空白文字に関する記述を削除
- P2348R3との比較を簡略化
- C++26 CDに採択されている他の提案との相互作用を記録
- 縦方向の空白文字の処理に関する今後の方向性を追加
このリビジョンでの変更は
- SG16のフィードバックを反映
- CWG2002解決を明記
- 最後のプリプロセッシングトークンが影響を受けない理由を明確化
などです。
C23の_BitIntをC++に導入する提案。
以前の記事を参照
このリビジョンでの変更は
- §4. Core design にSG22/SG6の投票結果を追加
- §4.8. Raising the BITINT_MAXWIDTH で提案されていた代替値
BITINT_MAXWIDTHを65535から32767へ変更
- SG22のフィードバックにより、§5.1. Naming of the alias template を拡張
- §5.4. Preserving integer-class types を追加し、それに伴い文言を変更
- §5.6. New abs overload および本文全体における戻り値型の欠落を修正
- §5.15. Passing bit_int into standard library function templates を拡張し、戻り値型に関する考察を追加
- §5.16. The problem of representing widths as int を追加
- §5.17. Library policy for function templates accepting bit_int を追加し、提案されたポリシーを §5.6. New abs overload に適用
- 参照をN3699 から N3747に更新
<cmath>だけでなく<cstdlib>にもstd::absのオーバーロードを追加
- § [utility.intcmp] を追加
などです。
erroneous behaviourにおける終了動作についての調整を行う提案。
以前の記事を参照
このリビジョンでの変更は
- NBコメントGB 02-036の解決を明記
- 文言の改善
などです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
ASCIIに関連する文字の判定・処理関数群を提供する提案。
以前の記事を参照
このリビジョンでの変更は
is_ascii_*関数をascii_is_*に変更
- §3.8. Naming を追加
- §3. Design において、複数のオーバーロードをすべての関数に対して単一の関数テンプレートに変更する
- §3.5. Case-insensitive comparison functions において、異なる文字型を混在させることがサポートされていない理由を明記
<ascii>のsynopsisにおけるascii_is_digitの不要なデフォルト引数を削除
- Cの用語に合わせるため、
is_ascii_printableをascii_is_printingにリネーム
- Cの用語に合わせるため、
is_ascii_graphicalをascii_is_graphicにリネーム
ascii_is_alphanumericとの不整合を回避するために、is_ascii_alphaをascii_is_alphabeticに変更する
- §5. Wording においてASCII-compatibleの定義を正しくする
などです。
P3064R2を要約した文書、および現在の実装ではOOTAが起こらないことを明確化する提案。
以前の記事を参照
このリビジョンでの変更は
などです。
ユニコード文字型の間の暗黙変換を非推奨にする提案。
以前の記事を参照
このリビジョンでの変更は
char8_tとwchar_tの間の変換の非推奨化を撤回
- 文言の改善
などです。
商と剰余を様々な丸めモードで計算するライブラリ関数の提案。
以前の記事を参照
このリビジョンでの変更は
div_to_even, div_to_odd, div_rem_to_even, div_rem_to_oddを削除
- §4.4.4. ISO/IEC 60559 Rounding modes を追加
div_to_infをdiv_to_pos_infにリネーム
などです。
トリビアルな共用体を定数式で使用可能にするために制限を緩和する提案。
以前の記事を参照
R0では次の3点を提案していました
- 共用体の最初のメンバの生存期間を暗黙的に開始するP3074ルールの撤回
- 集成体メンバ変数に対する
placement newは集成体そのものの生存期間を暗黙的に開始する
- 構成要素の値(constituent value)のルールを変更し、共用体の配列要素が(親の共用体or配列の)生存期間外にある場合でも扱えるようにする
このリビジョンでは、1を維持して、2をP3074R0で提案された解決策に変更しています。すなわち、std::start_lifetime()を再導入し、暗黙の生存期間をこの関数によって明示的に開始するようにします。
中間イテレータを指定する必要があるアルゴリズムについて、その必要のないバージョンを追加する提案。
以前の記事を参照
このリビジョンでの変更は
- SG9のフィードバックを反映
- サフィックス
_nを_at_mostに変更
- §4.2. Additional variants of
partial_sort を追加
- 文言から不要な
Sizeテンプレート引数を削除
などです。
std::arrayの実装自由度を制限する提案。
以前の記事を参照
このリビジョンでの変更は
などです。
std::inplace_vectorにおいて、std::optional<T&>を利用して堅牢化モードを導入する提案。
以前の記事を参照
このリビジョンでの変更は
などです。
ポインタのProvenanceを扱うためのライブラリAPIの提案。
CHERI(Capability Hardware Enhanced RISC Instructions)という技術が組み込まれたCPU(ISA)においては、ポインタにいくつかのメタデータを埋め込むことで、ポインタを介したメモリアクセスにおいてメモリ安全性の問題が起こることをハードウェアレベルで防止することができます。
CHERIのメタデータにはいくつかのフィールドがありますが、その内のタグビットはポインタ(CHERIのメタデータ)の改竄を防止するためのフィールドであり、ポインタをコピーしたりする際に自動でセットされ、CHERIメタデータの一部を上書きすると削除されるなどの性質を持ちます。これは結果的に、ポインタのProvenanceの性質のハードウェアによる表現になっています。
CHERIはすでにいくつかのアーキテクチャに組み込まれて実装されており、LLVM/Clangの対応を通してCとC++の実装がすでに確立されています。CHERI環境のC/C++実装では、CHERIメタデータはポインタを用いて表現可能である一方で、ポインタからアドレスを取得してそれを整数として扱う処理においては現在のC++のモデルのままだと問題があることが分かっています。
それは、ポインタ(アドレス)を整数値に変換する際に、後でポインタに戻される操作とポインタに戻されない操作の2つを区別できない点で、現在のC++実装及びモデルでは、どちらの場合も後でポインタに戻される操作とみなして過度な最適化を制限します。
CHERI C++におけるポインタ型はメタデータが追加された分大きくなっており、ポインタのうちアドレスが占める部分はおよそ半分になります(例えばvoid*128bitに対して、size_t64bit)。それにより、Provenanceが不要である場合の整数型はポインタ型のサイズに対してその差の分小さくすることができます。逆に、Provenanceが必要である場合はメタデータを保持する必要性からポインタ型と同じサイズを維持する必要があります。このため、最適化の観点から、ポインタから変換された整数値のうち後でポインタに戻さないもの、を区別できる必要が生まれます。
CHERI C++では、後からポインタに戻されない操作(変換)を区別するためにptraddr_tという専用の整数型を用意しています。ポインタ値がptraddr_tの値に変換された後でポインタに戻されても、デリファレンスできない(CHERIの仕組みによる拒否される)ポインタが生成されることが保証されます。すなわち、ポインタをptraddr_tに変換するということは後からポインタに戻さない事を表明します。
逆に、あとからポインタに戻すような整数値への変換は、現在のC++コードで記述しているのと同じように行えます。すなわち、CHERI C++においてのデフォルト動作はポインタの整数値への変換はあとからポインタに戻すことを仮定するものです。これはポインタの整数型への変換でも同じです。
したがって、CHERI C++のようなハードウェアによるProvenance実装を活用するためには、通常のポインタと整数型の経路とは別の経路でもって、後からポインタに戻されない変換とその操作を区別する必要があります。この提案はこのサポートのためのライブラリAPIを追加しようとするものです。
提案ではまず、ptraddr_t型を導入します。これは、provenance-freeの整数型であり、次の特性を持つものです
- 異なるオブジェクト(のポインタ)からの
ptraddr_t値は異なる値となる
- 同一オブジェクト内の異なるオフセットを指す2つのポインタ(メンバポインタ相当)は、異なる
ptraddr_t値を生成する
- 2つのポインタ型を
char*に変換し一方から他方を減算した結果は、両方のポインタをまずptraddr_tに変換してから減算した結果と等値になる
ptraddr_tからポインタ型へのキャストは未定義動作となる
- 実装ではこれらの操作に対して診断を行うことが推奨される
ポインタ型とのサイズ比の違いや後方互換のために、ptraddr_tはsize_tとは異なる型として定義される必要があります。
前述のように、ポインタからptraddr_tの値を得るには通常の変換ではなく専用の関数による変換を必要とします。
using ptraddr_t = ;
ptraddr_t get_address(void* p) noexcept;
ポインタpからget_address(p)で整数を取得することは、この整数値を後からポインタに戻すことは無いことを表明します。
ポインタを数値として扱う際に最適化を最大限に活用しつつも、なお後からポインタに戻したいユースケースがあるようです。そのために、ptraddr_tの値をポインタに復帰する関数も用意されます。
template <typename T>
T* with_address(T* p, ptraddr_t address);
template <typename T, typename UF>
constexpr T* transform_address(T* p, UF f)
requires (invokable<UF, ptraddr_t> &&
same_as<invokable_result_t<UF, ptraddr_t>, ptraddr_t>)
{
return with_address(p, f(get_address(p)));
}
CHERI C++におけるポインタはCHERIのメタデータを内包しています。メタデータにはタグビットとアドレス以外にも権限や型の互換性を表すフィールドがあります。そのため、アドレス部分のみが取り出されているptraddr_t値をポインタに戻すためにはそのほかの部分のメタデータが必要となり、with_address(p, addr)はaddrにpのメタデータを合成してポインタを復帰する関数です。
ただし、with_address()を用いて間違ったメタデータによって復帰されたポインタのデリファレンスは未定義動作となります。
transform_address()はポインタからptraddr_t値を取得して何か処理をしてまたポインタに戻す、という操作を複合的に行うためのユーティリティです。
この2つの関数は、アトミック操作におけるCASやXORリンクリストなどのように途中で整数値として扱いつつも最後はポインタに戻す場合でパフォーマンスが重要となる用途で使用できるものです。
CHERIのような環境におけるアトミック操作の効率化のために、atomic_refやatomicのT*特殊化に対してcompare_exchange_(weak|strong)を追加することも提案しています。
template <typename T>
class atomic_ref<T*> {
constexpr bool compare_exchange_weak(
T*&, T*,
T*& provenance_target,
memory_order, memory_order
) const noexcept;
constexpr bool compare_exchange_strong(
T*&, T*,
T*& provenance_target,
memory_order, memory_order
) const noexcept;
constexpr bool compare_exchange_weak(
T*&, T*,
T*& provenance_target,
memory_order = memory_order::seq_cst
) const noexcept;
constexpr bool compare_exchange_strong(
T*&, T*,
T*& provenance_target,
memory_order = memory_order::seq_cst
) const noexcept;
};
これらのアトミック操作はABA問題による未定義動作を回避しつつ、ptraddr_tを使用した最適化を適用するものです(提案には実装例があります)。
また、現在その操作がほぼ実装定義とされているintptr_t/uintptr_tの値に対する操作について、ポインタ演算との等価性を保証することを提案しています。ただしこれは、ポインタから変換されたintptr_t/uintptr_tの値に対する操作についてのみ保証し、無関係なintptr_t/uintptr_tの値の演算はptraddr_tの演算と同じ意味論を持つべきとしています。
↓
ARMv8.3で導入されたPointer Authentication Codesと呼ばれる機能についての解説文書。
Pointer Authentication Codesとは、ポインタに対してコンテキストに応じた認証を行い、その認証コードをポインタのビット内に埋め込んで利用する仕組みの事です。これは現在ARMv8.3以降のISAで実装され、利用されています。
Pointer Authentication Codesの存在は、トリビアルリロケーションやポインタの利用されないビットを利用できるようにする提案に影響を与えているものの、まだ登場したばかりの技術であるため知名度が高くありません。
この提案はPointer Authentication Codesの概要と、Clangにおけるサポートとモデル化について解説するものです。特に、C++言語機能や型システムに対してどのような影響を与え、また与えないのか、について重視しています。
標準のview型がsize()を提供している場合にreserve_hintを削除する提案。
以前の記事を参照
このリビジョンでの変更は
take_viewについて、そのベースの範囲がapproximately_sized_rangeでない場合にのみreserve_hint()を有効化する
などです。
この提案については、関連するNBコメントがリジェクトされたようで、この提案の追及も停止されている可能性があります。
<bit>のビット置換系操作APIのstd::simdオーバーロードを追加する提案。
以前の記事を参照
このリビジョンでの変更は
- 参照する提案の番号を修正
simd::bit_repeat()の不要なnoexcept削除
- N5014にリベース
などです。
整数のビットシフトを行うライブラリ関数の提案。
以前の記事を参照
このリビジョンでの変更は
- シフト量の引数を、任意の整数型を取れるようにした
- 負のシフト量に対する操作を、逆方向のシフトにした
- §4.3.1. Emulating possible behaviors using
std::shl を追加
- §4.3.2. Benchmarks of std::shl variants を追加
- §5. Possible implementation を追加
- N5014にリベース
などです。
parallel_schedulerの設計を洗練させるための提案。
以前の記事を参照
このリビジョンでの変更は
- SG1+LEWGからのフィードバックを反映
- P3826R2に基づいて、文言を修正
などです。
P3149の非同期スコープ機能に、scope_associationコンセプトを追加する提案。
以前の記事を参照
このリビジョンでの変更は
- フィードバックを受けて、背景と同期に関するセクションを強化
などです。
meta::infoのハッシュサポートを追加する提案。
以前の記事を参照
このリビジョンでの変更は
- ハッシュの様々なセマンティクスと、現在の動作を選択した理由について説明するセクションを追加
- ABIに関する潜在的な懸念事項について説明するセクションを追加
- 初期設計とサポートに関する潜在的な拡張についてのセクションを追加
- モチベーションをさらに追加
などです。
初期化子リストの補助配列に対して、静的記憶域期間を義務付ける提案。
以前の記事を参照
このリビジョンでの変更は
などです。
理由は分かりませんが、この提案はリジェクトされているようです。
↓
senderアルゴリズムのカスタマイズをC++29まで延期する提案。
以前の記事を参照
R1での変更は
- C++26のアルゴリズムのカスタマイズ方法を改善する代替案を提示
このリビジョンでの変更は
indeterminate_domain<>を追加
などです。
R1で追加された代替案のカスタマイズ方法の修正は、senderに対して完了場所を問い合わせる際は開始場所を明示するようにすることです。元々、senderは自身がどこで開始されるかを知らない限りどこで完了するかが分からない、という点が問題だったため、完了場所を問い合わせる際に開始場所を指定するようにすることで解決します。
これはsenderに対して完了ドメインを問い合わせる際にreceiverの環境を渡すことで実現されます。get_domain(get_env(sndr))の代わりにget_completion_domain<set_value_t>(get_env(sndr), get_env(rcvr))のようにクエリするようにします。
この変更ではクエリの際にreceiverの環境が必要となるためEarly customizationは削除されます。
この修正はC++29を目指す場合でもおそらくほぼ同様となります。この変更は大きな設計変更でありC++26のこの時期に入れるにはリスクがあるとして、C++26ではカスタマイズについてを削除し、C++29で修正しつつ復帰という当初の案がメインの提案です。
複数のmutexに対してより柔軟なロックをかけることのできるstd::multi_lockの提案。
C++26時点では、標準ライブラリには次のミューテックスラッパクラスが用意されています
std::lock_guard
std::unique_lock
- 単一ミューテックス用の柔軟なRAIIラッパ
- 遅延ロック、ロック取得の試行、所有権移転などの機能を備えている
std::scoped_lock
- 複数のミューテックス用のRAIIラッパ
- デッドロック回避機能を備えている
ただ、ここにはstd::scoped_lockのように複数のミューテックスを取りながらstd::unique_lockのように柔軟で多彩なロックをサポートしているようなものがありません。
表に起こすと、次の表の右下の部分をカバーするものがありません。
| 性質\ミューテックス数 |
単一ミューテックス |
複数のミューテックス |
| 常に所有する(ムーブ不可) |
std::lock_guard |
std::scoped_lock |
| 柔軟なロック制御方法 |
std::unique_lock |
|
このため、この提案ではそのような特徴(std::scoped_lockとstd::unique_lockの両方の利点)を持つ(上記表の右下の部分をカバーする)新しいミューテックス用のRAIIラッパ型であるstd::multi_lockを追加しようとしています。
namespace std {
template<class... MutexTypes>
class multi_lock;
}
この提案のstd::multi_lock<Ms...>は次のような特徴を持ちます
- 0個以上のミューテックスを受け入れる
- RAIIによる自動ロック解除機能を提供
- 遅延ロック、
try-lock, adopt-lock戦略をサポート
- 時間制限付きロック操作をサポート
- すべてのミューテックスが
Cpp17TimedLockableの場合
- ムーブセマンティクスによる所有権移転をサポート
遅延ロックの例
std::mutex m1, m2;
std::multi_lock lock(std::defer_lock, m1, m2);
if (condition) {
lock.lock();
}
時間制限付きロックの例
std::multi_lock lock(100ms, m1, m2);
if (lock) {
}
手動制御による条件付きロックの例
std::multi_lock lock(std::try_to_lock, m1, m2);
if (!lock) {
return;
}
複合代入演算子をデフォルト実装できるようにする提案。
以前の記事を参照
このリビジョンでの変更は
B&で渡される右辺オペランドの許容範囲を拡大
- § 4.1 Minor Edge Casesで、列挙型のステータスを明確化
noexceptに関する説明を追加
- 投票結果を追加
などです。
この提案はEWGのレビューにおいてリジェクトされているようです。
std::optional<T&>がトリビアルコピー可能であることを規定する提案。
以前の記事を参照
このリビジョンでの変更は
- タイトルにNBコメント番号を追加
- LEWGの投票に従い、オプション1のみを提案する
- オプション2はトリビアルコピー可能であることに加えてポインタとレイアウトが同じになることを指定するもの
などです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
↓
std::nontypeをstd::constant_argにリネームしないようにすることを推奨する提案
以前の記事を参照
R1での変更は
- 現在の方向性を反映して、タイトルを変更
std::nontype_tの完全な代替案としては提案しなくなった
などです。
↓
std::simdの暗黙変換が可能なブロードキャストコンストラクタを復帰させる提案。
以前の記事を参照
R1での変更は
constevalコンストラクタをbasic_vecの値型に自然に変換可能な算術型に限定する
common_typeへの影響について議論を追加
- [simd.math]への影響について議論を追加
constevalコンストラクタをimmediate-escalating expressionとして扱うことについて議論を追加
- [simd.math]への潜在的な影響について議論を追加
このリビジョンでの変更は
- 提案していた投票を削除
- 不要な
simd-broadcast-argを削除するように文言を更新
- [simd.math]の文言を元の設計意図(明示的なオーバーロードセットであるかのように扱う)に合わせるように変更
- [simd.math]の文言に対する修正/改善
math-common-simd-t<V0, V1>がスカラ型を含むデフォルト以外のABIタグの組み合わせで動作しなかった
などです。
ラムダ式のクロージャ型において、明示的なキャプチャの初期化順序を規定する提案。
ラムダ式のクロージャ型は、そのメンバの宣言順は未規定でありメンバ変数の初期化順も未規定とされています。これは初期化キャプチャが導入されるよりも前からの仕様で、おそらく暗黙的なキャプチャの使用順序を追跡しない実装を許可するための自由度だったと思われます。
しかし、このような自由度は現在の実装よって使用されておらず、仮に使用したとしても有用なメリットが無さそうです。例えば、最も可能性がありそうなのはクロージャ型のパディングを最小化することですが、クロージャ型のメモリフットプリントが問題になることはめったにありません。
一方で、次のようなコードを合法ではなくしています
void construction() {
auto x = std::make_unique<int>();
[value = *x,
lifetime = std::move(x)
] {};
}
struct last_call {
std::function<void()> f;
~last_call() {f();}
};
void destruction() {
auto x = std::make_unique<int>();
const auto user = [&i = *x] {std::cout << i << '\n';};
[lifetime = std::move(x),
trigger = last_call{user}
] {};
}
特にこのように、初期化キャプチャにおいてはデフォルトメンバ初期化子などと類似していることもあり、書いた順序と異なる順序で初期化される場合はプログラマの意図とは明らかに異なるでしょう。
ただ現在のところ、主要な実装はこの自由度を利用していません。すべての主要な実装では、すべての非参照明示的キャプチャ(簡易キャプチャと初期化キャプチャ)の順に対応するメンバが定義され、その後に暗黙キャプチャがその使用順(キャプチャの出現順)に定義され、初期化はその順序で行われるようです。
また、Itanium ABIではクロージャ型のレイアウトにこの順序を使用して規定を行う予定があるようです。
そのため、この提案ではこれらの既存実装における慣例に従って、明示的なキャプチャ(簡易キャプチャと初期化キャプチャ)はすべてその出現順に宣言され初期化されるように規定することを提案しています。そして、これを欠陥報告(DR)とすることも提案しています(おそらくC++14)。
ただしここでは、暗黙キャプチャについての順序を明記することは提案していません(EWGが望むのであればそれも可能とはしていますが)。なぜなら、デフォルトキャプチャがある場合にソースコード順と異なる結果をもたらすためです。
またついでに、参照初期化キャプチャ用のメンバが存在しない場合にその初期化方法について全く指定がないという別の問題も修正しています。
EWGのレビューでは、この提案をDRとしてC++29ターゲットで導入することに合意が取れているようです。その際、暗黙キャプチャの順序を含むようにはしていないようです。
C++26 Contractsをホワイトペーパー等を経由するようにする提案。
以前の記事を参照
このリビジョンでの変更は
- 参考文献と補足説明を追加
- dependency セクションを拡張
- marketing に関するセクションを追加
などです。
この提案はEWGのレビューにおいて賛同を得られず、リジェクトされています。
あるポインタが別のポインタ範囲内にあるかどうかをチェックする関数の提案。
何かしらオブジェクトや配列要素を指すポインタがあり、それが別のポインタによって構成される範囲内に含まれているかを調べたい、あるいは出自の分からないポインタが2つあるときにその等価性を調べたい、という場合がたまにあります。しかし、標準の範囲内でこのような比較を行う方法は用意されていません(直接の比較結果は未規定)。
一応、std::less<T*>などでは実装定義の全順序でポインタの比較を行うことができるため、実装のサポートがあればそのようなチェックを行うことはできます。しかしこの方法は定数式で使用できません。
この提案では、実装定義の全順序に依存しない形で直接的にポインタ同士の関連性をチェックすることのできる関数を標準ライブラリで用意しようとするものです。
提案しているのは次のような関数です
namespace std {
constexpr bool is_pointer_between(const void * ptr, const void * first, const void * last);
}
std::is_pointer_between()はシグネチャ通り、第一引数に渡したポインタ値が、第二・第三引数のポインタ範囲内に含まれる場合(first <= p and p < lastの場合)にtrueを返す関数です。
この関数は実装に全順序を要求することなく使用でき、定数式でも使用可能なものです。ただし、その実装はおそらくコンパイラマジックが必要になります。
これを用いると例えば、契約アサーションによってポインタの包含をアサートできるようになります。
constexpr bool intersect(std::span<const T> lhs, std::span<const T> rhs) noexcept {
const auto * a = lhs.data();
const auto * b = lhs.data() + lhs.size();
const auto * x = rhs.data();
const auto * y = rhs.data() + rhs.size();
return std::is_pointer_between(x, a, b)
|| std::is_pointer_between(y, a, b)
|| std::is_pointer_between(a, x, y)
|| std::is_pointer_between(b, x, y);
}
void transfer(std::span<const T> source, std::span<T> target)
pre(not intersect(source, target))
pre(source.size() == target.size())
{
std::copy(source.begin(), source.end(), target.begin());
}
また、std::hiveの.get_iterator(T*)のconstexpr化のためにも使用できるとのことです。
↓
↓
ある型が構造化束縛によって分解可能であるかを取得する関数の提案。
以前の記事を参照
R1での変更は
- 提案番号を修正
- proof of concept (section 3.3)からの考察を追加
R2での変更は
このリビジョンでの変更は
などです。
is_structural_type()は構造的な型(NTTPとして使用可能な型)を判定するリフレクションメタ関数です(名前は似ていますが役割はかなり異なります)。
指定された場所でオブジェクトの生存期間を再開するstd::restart_lifetimeの提案。
以前の記事を参照
このリビジョンでの変更は
- 型消去のセクションにApplicationを追加
std::restart_lifetimeの冗長な呼び出しがサポートされていることを明確化
- 関連するNBコメントへの参照を追加
などです。
C++26からトリビアルリロケーション関連が削除されたことを受けて、この提案の議論は一旦停止されています。
std::atomic_ref<T>においてstd::atomic_ref<const T>への変換コンストラクタを追加する提案。
以前の記事を参照
このリビジョンでの変更は
noexceptを追加
- 制約の記述を“T and U are similar types”に変更
- 整数型/浮動小数点数型/ポインタ型の特殊化に変換コンストラクタを追加
などです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
モジュール宣言の前に#lineディレクティブを置けるようにする提案。
以前の記事を参照
このリビジョンでの変更は
- groupを使用しないように文言を変更
- 別の編集上の変更との互換性を持たせる
などです。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
↓
P3666R1の紹介スライド。
P3666R1については以前の記事を参照
P3666R1ではC23で追加された__BitIntをC++でもサポートしようとしています。このスライドではどのようにそれを輸入してくるか(ほぼCそのまま+C++標準ライブラリでの対応)について簡単に説明されています。
std::from_chars/std::to_charsでchar以外の文字型による文字列をサポートするようにする提案。
std::from_chars/std::to_charsは文字列と数値間の変換を行うもので、特にパフォーマンスに重きを置いています。これらの関数は入力/出力の文字列としてcharの文字範囲のみをサポートしています。char8_tを始めとしてC++にはchar以外の文字型がありますが、これらの文字型による文字列のサポートは行われていません。
この提案は、std::from_chars/std::to_chars両関数でchar以外の文字型による文字列を入出力としてサポートすることを提案するものです。
特に、std::format()における数値から文字列への変換はstd::to_charsを用いて規定されているため、std::formatがchar8_t等のユニコード文字型をサポートするために重要なステップとなります。
この提案の対象の文字型はchar以外のすべてのもの
char8_t
char16_t
char32_t
wchar_t
です。
wchar_tを除けばこれらの文字型はすべてユニコードのエンコーディングの一つと対応しており、ユニコード(UTF)ではASCII範囲の文字が数値的にASCII互換になっているため、ほとんど特別な処理は不要です。
この提案では、ユニコード文字型のstd::from_chars/std::to_charsオーバーロードは共にcharオーバーロードの出力形式と互換となるようにしています。例えば、std::from_charsにおいてASCII範囲外の数字とみなせる文字(Ⅳなど)を数値として変換できるようにすることは提案していません。
また、ユニコードのエラーハンドリングもほとんど行わないことを提案しており、この提案のオーバーロードはあくまでユニコード文字列内のASCII互換数字の範囲内(基本ラテン文字のブロック内)でのみ動作します。すなわち、std::from_charsでは基本ラテン文字のブロック外の文字コードはすべて解析パターンではないものとして扱われます。
string_view cstr = "123z";
int i1;
const auto [p1, e1] = from_chars(cstr.data(), cstr.data() + cstr.size(), i1);
u8string_view u8str = u8"123\xFF";
int i2;
const auto [p2, e2] = from_chars(u8str.data(), u8str.data() + u8str.size(), i2);
assert(i1 == i2);
assert(p1 - cstr.data() == p2 - u8str.data());
これらのことは、std::from_chars/std::to_charsは共にパフォーマンスを最重視する低レベルなユーティリティであることを反映しています。
この提案ではむしろ、既存のcharオーバーロードの互換性を崩すことなく新しいオーバーロードを追加する方が困難だとしています。なぜなら、std::from_chars/std::to_charsはchar版だけでも11個のオーバーロードがあり、単純に文字型毎にオーバーロードを定義すると55のオーバーロードを追加することになってしまうため、テンプレート化が必須となるからです。
テンプレート化すると特に問題があるのは戻り値型です。std::to_chars_result/std::from_chars_resultはどちらも、入出力文字列の位置に関する情報を保持するために文字へのポインタ(char*/const char*)をメンバとして持つ集成体型でありテンプレート化されていないためです。名前も最も適切なものを占有しています。
既存のこれらをテンプレート化することはできないため、元の型に変更をくわえず新しいテンプレート化された結果型を使用するために、この提案ではエイリアステンプレートを使用するようにしています。
提案より、新しいstd::to_charsセットの概要
template<class T>
concept character-type = any character type;
struct to_chars_result {
char* ptr;
errc ec;
friend bool operator==(const to_chars_result&, const to_chars_result&) = default;
constexpr explicit operator bool() const noexcept { return ec == errc{}; }
};
struct wto_chars_result { };
struct u8to_chars_result { };
struct u16to_chars_result { };
struct u32to_chars_result { };
template<character-type T>
using to_chars_result_t = one of the result types above;
constexpr to_chars_result
to_chars(char* first, char* last, integer-type value, int base = 10);
template<character-type T, integer-type-concept U>
constexpr to_chars_result_t<T> to_chars(T* first, T* last, U value, int base = 10);
to_chars_result to_chars(char* first, char* last,
floating-point-type value);
template<character-type T, floating-point-type-concept U>
to_chars_result_t<T> to_chars(T* first, T* last, U value);
to_chars_result to_chars(char* first, char* last,
floating-point-type value, chars_format fmt);
template<character-type T, floating-point-type-concept U>
to_chars_result_t<T> to_chars(T* first, T* last, U value, chars_format fmt);
to_chars_result to_chars(char* first, char* last,
floating-point-type value, chars_format fmt, int precision);
template<character-type T, floating-point-type-concept U>
to_chars_result_t<T> to_chars(T* first, T* last, U value, chars_format fmt, int precision);
新しいstd::from_charsセットの概要
struct from_chars_result {
const char* ptr;
errc ec;
friend bool operator==(const from_chars_result&, const from_chars_result&) = default;
constexpr explicit operator bool() const noexcept { return ec == errc{}; }
};
struct wfrom_chars_result { };
struct u8from_chars_result { };
struct u16from_chars_result { };
struct u32from_chars_result { };
template<character-type T>
using from_chars_result_t = one of the result types above;
constexpr from_chars_result
from_chars(const char* first, const char* last, integer-type& value, int base = 10);
template<character-type T, integer-type-concept U>
constexpr from_chars_result_t<T> from_chars(T* first, T* last, U& value, int base = 10);
from_chars_result from_chars(const char* first, const char* last,
floating-point-type& value,
chars_format fmt = chars_format::general);
template<character-type T, floating-point-type-concept U>
from_chars_result_t<T> from_chars(T* first, T* last, U& value,
chars_format fmt = chars_format::general);
また、この提案では追加されるwchar_tオーバーロードのstd::to_charsを用いるようにstd::formatの規定を修正することも提案しています。std::formatはすでにwchar_tをサポートしていますが、その規定でもstd::to_charsが使用されています。これを額面通りに実装するとstd::to_charsの出力をwchar_t文字範囲に書き込むことになりますが、このような実装は存在しない事を暗に仮定してもいます。この提案により、std::to_charsがほぼそのまま使用できるようになります。
SG16による初期レビューではこの提案の方向性(std::from_chars/std::to_charsの両方で4つの文字型のサポートを追加)におおむねコンセンサスが取れています(それでも反対意見はあったようです)。
↓
標準ライブラリの堅牢化モードは終了セマンティクスで評価されることを必須とする提案。
C++26から導入される標準ライブラリの堅牢化モードは、Contractsの枠組みの中で標準ライブラリの一部の関数呼び出しにおいて事前条件をチェックするモードです。
Contractsの仕様では、アサーションがチェックされてからどうするか(特に終了するかしないか)については評価セマンティクスというもので主に指定されます。評価セマンティクスには終了セマンティクス(enforce, quick enforce)と非終了セマンティクス(ignore, observe)の大きく2種類があります。
現在の堅牢化モードでは少なくとも契約アサーションの条件がチェックされるセマンティクス(enforce, quick enforce, observe)が選択されることは規定しているものの、どのセマンティクスを選択するかは実装に委ねています。
この提案では、堅牢化モードにおける評価セマンティクスとしてobserveを禁止し、確実に終了セマンティクスで評価を行うようにすることを提案しています。
主な理由は、observeは契約違反を検出しても終了せずに継続するため、標準ライブラリの事前条件チェックにおいては確実に未定義動作に陥るためです。このような振る舞いは堅牢化(hardened)モードという名前にふさわしくないため、堅牢化モードでは禁止すべきという主張です。
この提案はobserveのユースケースを軽視しているのではなく、堅牢化モードという名前を名乗るべきではないとするもので、実装が「チェックされるが堅牢ではない(checked but not hardened)」モードを提供することには反対していません。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
spanのメンバ関数を、コンパイル時定数を引数として取ることができるようにする提案。
C++26では、std::cwによってコンパイル時引数を比較的簡単に実現できるようになりました。この提案は、これを用いてstd::spanの.subspan()などのサイズやインデックスの指定をコンパイル時定数によって指定できる様にしようとするものです。
int arr[42] = {};
std::span sp(arr);
auto first5 = sp.first (std::cw<5>);
auto last5 = sp.last (std::cw<6>);
auto sub = sp.subspan(std::cw<2>, std::cw<9>);
対象は.first(), .last(), .subspan()の3つです。
これらの関数は現在実行時引数を取るため、静的エクステントを持つspanであっても動的エクステントのspanしか取り出せません。また、境界チェックを行おうとしても実行時にしか行えません。
コンパイル時定数を取れるようになれば、可能な限り静的エクステントの情報を保持できるようになります。
また、これによりstd::mdspanとの一貫性も高まります。std::mdspanに対するstd::submdspan()では、そのスライスの指定などに整数定数だけではなくstd::cwのようなコンパイル時定数を使用できるようになっていますが、std::spanではそうなっていないためです。この目的のために、この提案において使用可能なコンパイル時定数とはstd::cwに限りません(std::mdspanと同様にintegral-constant-likeという説明専用コンセプトで指定されています)。
全ての副作用の無い無限ループを未定義動作ではなくする提案。
C++では副作用のない(=終了の見込みがない)無限ループは未定義動作とされています。これはマルチスレッドプログラムにおいてプログラムが終了に向かって進行することを保証するための規則(forward progress guarantees)であり、この未定義動作を利用してコンパイラはそのようなループが終了することを仮定した最適化を行うことができます。
この自由度はコンパイラに最適化機会を提供する一方で、プログラマには不便を強いています
- 不可解な最適化の可能性
- 副作用の無い無限ループを削除するためにnullチェックなどの重要なコードが削除される可能性がある
- UBを回避するための冗長コードの必要性
また、コンパイラの実装に対してもC++特有のこのようなループセマンティクスを実現するための実装を維持する負担を強いています(例えばLLVM のmustprogress)。C/Rustではこのような規定は存在しないため、無限ループの扱いおよび最適化のためのインフラがC++だけ別に必要になる部分があります。
このようなセマンティクスの違いはRustとC++およびCの間で一貫しない最適化結果をもたらし、相互運用性を妨げます。
肝心のコンパイラの最適化についても、ほとんど利用されていないどころか、利用しない方がプログラムのパフォーマンスが向上する可能性があることが示されています。
これらのことからこの提案では、副作用の無い無限ループを未定義動作とするのをやめて、単にwell-definedにすることを提案しています。
現在次のようなプログラムは未定義動作となっていますが、この提案後はそうではなくなります。
int main(void) {
bool True = true;
while(True);
return 0;
}
次のプログラムを実行しても終了したりUBが発生したりしません。
int main() {
while(true);
*nullptr;
return 0;
}
次のようなマルチスレッドプログラムでは、abort()が呼ばれるとは限らなくなります。
#include <atomic>
#include <thread>
std::atomic<bool> flag = false;
void thread0() {
flag.store(true);
abort();
}
void thread1() {
flag.wait(false);
bool True = true;
while(True);
}
int main() {
std::jthread t0(thread0);
std::jthread t1(thread1);
return 0;
}
このサンプルのように、いずれかのスレッドが実行ステップを実行しない場合でも、実装は弱い並行進行保証(weakly parallel forward progress)を提供することができます。これにより、現在未定義動作となっているコード(副作用の無い無限ループ)は未定義動作ではなくなります。また、この提案の後も現在未定義動作ではないコードの動作は変更されません。
bool値を反転させる演算子の提案。
bool値を反転させるためにはflag = !flag;の様な一文を書かないとなりませんが、変数名が2回現れるなど冗長になります。
一方、このようなbool値の反転はboolフラグによって状態を管理するような場所で頻出するため、使用したいシーンは多岐にわたります。
この提案では、これをインクリメント演算子などのようにin-placeかつ簡潔に行うための新しい演算子として、後置~を提案しています。
bool flag = true;
flag~;
この演算子は次のような特徴を持ちます
- 後置演算子
- オペランド対象は
boolの左辺値のみ
- 式の型は
void
if (flag~)の様な紛らわしい使用を回避するため
前置~は整数型に対するビット反転演算子としてすでに競合していますが、後置~は現在無効な構文となっているため後方互換性を保ったまま導入することができます。また、後置であることによって後置インクリメント/デクリメント演算子との動作が一貫し、in-placeで値を変化させていることが明確になります。
P3505R2の紹介スライド。
P3505R2で提案されているstd::formatにおける正しい浮動小数点文字列化における問題やその背景、パフォーマンスへの影響などが詳しく説明されています。
std::error_categoryをフォーマット対応させる提案。
現在のところ、std::error_categoryを直接フォーマットや出力する方法がありません。
std::cout << std::generic_category();
std::print("{}", std::generic_category());
P3395で解決されたものの、std::error_category::name()にはエンコーディングが指定されていないという問題もありました。
この提案は、std::format()/std::printにてstd::error_categoryを直接扱えるようにしようとするものです。
std::print("{}", std::generic_category());
std::print("[{:>10}]\n", std::generic_category());
generic
[ generic]
std::error_categoryはstd::string_viewのフォーマッタを利用してフォーマットされるため、文字列と同じオプションが使用可能です。
iostreamに対する対応は行われていませんが、std::format()を通して出力することは可能になります。
契約違反ハンドラの置換がサポートされているかどうかをチェックする方法を提供する提案。
C++26 Contracts仕様では契約違反ハンドラの置換がサポートされているものの、そのサポートは実装定義とされています。とはいえ、実装がサポートしているかは文書を当たるしかなく、コードからそれを調べる方法はありません。サポートされていない場合にコンパイルエラーになる保証はなく、ほぼ未定義動作に近い扱い(ill-formed, no diagnostic required)になります。
#if __cpp_contracts
#include <contracts>
void handle_contract_violation(
const std::contracts::contract_violation &
);
#endif
この提案では、これを回避するために、違反ハンドラの置換可能性を判定するための機能テストマクロを提供する様にするものです。提案しているのはライブラリマクロの__cpp_lib_replaceable_contract_violation_handlerです。
#if __cpp_contracts
#include <contracts>
#if __cpp_lib_replaceable_contract_violation_handler
void handle_contract_violation(
const std::contracts::contract_violation &
);
#endif
#endif
__cpp_lib_replaceable_contract_violation_handlerの値が0ではない場合は違反ハンドラの置換がサポートされていることを表しています。
この提案はNBコメントを受けてそのための文言を提供するためのものです。LEWGでは合意が得られたようですが、CWGでの合意を得ることができなかったようです(リジェクトされてはいないようですが)。
↓
execution::when_allの停止完了に関する仕様を調整する提案。
execution::when_allは指定されたすべての子操作が完了(値・停止・エラー)してから自身も完了を報告する(子操作の完了を待機)するsenderアダプタです。when_allが完了するのはその名の通りwhen_allに指定されたすべての子操作(sender)が完了してからだと思うのが自然ですが、実際にはwhen_allが子操作を開始する前に停止要求を検出した場合にも停止完了をします。
しかもこの場合の停止が行われる際には子操作は一切開始されません。
この早期の停止要求検出機能は、子senderが全て停止完了を行わないことが静的にわかっている場合でも、子senderが全て停止トークンを認識しないことが分かっている場合でも、無条件で実行され、when_allのsenderは停止完了チャネルを持ちます。これによって、下流のreceiverに対して停止完了シグナルをサポートすることを暗黙的に要求しています。
これらのことはexecution::when_allという名前から想像できるその責務・動作とは異なり、その名前以上の事を行っており、単一責任の原則や驚き最小化の原則に従っていないと言えます。
この提案は、execution::when_allのこれらの問題を解消して、その名前の通りの予測可能な動作をするようにしようとするものです。
次の変更を提案しています
- 早期停止検出機能の削除
- 子操作の開始前に停止要求を確認するのをやめて、停止要求に関わらず常に子操作を開始するように変更する
- 停止完了チャネルを持つ条件の変更
- 現在は無条件で値無しの停止完了チャネルを持つが、少なくとも一つの子操作が停止完了チャネルを持つ場合にのみ
when_allも停止完了チャネルを持つように変更する
これによって、execution::when_allはその名前の通りに子操作を開始しその完了を待機するものになり、それ以上の副作用を持たなくなります。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
よりシンプルなcontracts機能の提案。
この提案ではP2900のContractsは複雑かつ実装定義が多すぎることで、MVPとはいえないとして、真にミニマルなContracts機能を示すものです。
ここでは次の2つの特徴を持つ契約機能を例として示しています
- 常にチェックされるアサーションが一種類だけある
- 契約違反ハンドラを設定できる
この提案は厳密にはこのような機能を提案しているわけではないですが、このようなシンプルなものでも想像するよりも多くの人に効果的にサービスを届けることができ、P2900はこのような点を見落としている、と主張しています。
未初期化メモリに対するアルゴリズムの並行アルゴリズム版記述を明確化する提案。
<memory>ヘッダに存在する未初期化メモリに対するアルゴリズムには、C++17以降並行アルゴリズムが追加されています。しかし、規格書においてこのことは一部の文章によって存在が示唆されるのみで、具体的な定義がされていませんでした。
この提案は、未初期化メモリに対する並行アルゴリズムの定義を明確化するものです。
std::uninitialized_default_construct
std::uninitialized_value_construct
std::uninitialized_copy
std::uninitialized_move
std::uninitialized_fill
std::destroy
同時に、並行Rangeアルゴリズムについても追加されています。
C++の文法規則を指定する構文において、繰り返しとリストを簡潔に表現するための記法を追加する提案。
C++の文法規則を標準内で指定するために使用されている構文記法はあまり機能が多くなく、次のような機能しかありません
- 連結
pp-number、identifier-continueなど
- 共用体
- オプション展開
特に、頻繁に使用されているにもかかわらず繰り返しとリストのための構文が欠けており、これによって半ばお決まりのパターンによる指定がなされています。
declaration-seq:
declaration declaration-seq(opt)
template-parameter-list:
template-parameter
template-parameter-list , template-parameter
この様な指定は読みやすさを損ね、構文規則を膨張させています。
この提案では、繰り返しとリストのための新しい記法を追加することでこのような冗長な表現を置き換えようとするものです。
ここで提案されているのは次の3種類です
Xseq
Xseq opt
- seq optは下付き文字
- Xの0回以上の繰り返し
⟪X yyy⟫
Xとyyyをグループ化することでopt, seq, seq optをグループに含まれるもの全てに対して適用できる
提案文書より、例(下付きのoptやseq optはmarkdownだと再現できないので、()でくくって表現しています)
| 現在 |
この提案 |
compound-statement:
{ statement-seq(opt) label-seq(opt) }
statement-seq:
statement statement-seq(opt)
label-seq:
label label-seq(opt)
|
compound-statement:
{ statement(seq opt) label(seq opt) }
|
| P3149R11 |
この提案 |
initializer-list:
initializer-clause ...(opt)
initializer-list , initializer-clause ...(opt)
|
initializer-list:
initializer-clause ...(opt) ⟪ , initializer-clause ...(opt) ⟫(seq opt)
|
| P3149R11 |
この提案 |
identifier:
identifier-start
identifier identifier-continue
|
identifier:
identifier-start identifier-continue(seq opt)
|
この提案は純粋にeditorialなものです。
停止要求が発行されていたら処理の開始を無視するsenderアルゴリズム、unless_stop_requestedの提案。
P3887R0(少し上の方)では、when_allが早期の停止要求検出機能を備えていることを問題視し、それを削除しようとしています。その動機付けの中で、早期の停止要求検出機能は単一のアルゴリズムとして簡単に実現できる、として自身の講演をリンクしています。しかし、使用例や実装例などは示されていません。
この提案は、この早期の停止要求検出とその時点での停止完了発行を行うアルゴリズムとして、unless_stop_requestedを追加するものです。
std::executionの停止要求(およびストップトークンの仕組み)は、C++20でstd::jthreadとともに追加された仕組みをベースとして構築されています。このstd::stop_tokenのフレームワークにおいては、停止シグナルのことを停止要求(stop request)と呼んでいます。ここで要求という言葉が採用されている意味は、停止要求が強いものではなく無視される可能性があるものだからです。
停止要求の処理は明確に協調的(cooperative)なものとして設計されており、それを受けて処理を停止するかどうかは要求を受けた側が決めることです。その根拠の一つとして、停止要求の受信とほぼ同時に非同期処理が完了(成功orエラー)した場合、停止要求は無視して本来の成功を返した方が何が起きたかの情報を伝達できることがあります。
これは単一のコンポーネントや小さな処理レベルでは良い選択となりえますが、より大きなシステムの構成要素となった時に停止要求を無視するコンポーネントは無限ループに陥るリスクがあり、システム全体を停止不可能な状態にしてしまいます。
これは標準ライブラリのような汎用的なコンポーネント(フレームワーク)では回避すべきでありそのための仕組みを最初から入れておくべき、という意見もありますが、停止要求のチェックと応答にはコストがかかるため、むしろ汎用的なフレームワークに入れてしまうと、フレームワーク内部の停止要求応答のためのコストはそこから構成されるコンポーネントの数に比例して増大していきます。
std::stop_tokenのフレームワークが任意になっているのはこうした事情からであり、この観点からもP3887R0の主張は理にかなったものと言えます。しかし一方で、ユーザーが簡単に停止要求への対応を行う方法を用意しておくことには特に問題はないはずです。そのような部品を提供しておけば、ユーザーがどの時点で処理の停止要求を考慮するかを完全に制御することができます。
execution::unless_stop_requestedはsenderアルゴリズムとしてまさにそのような働きをするもので、senderによるチェーンに挿入しておくことでその処理ステップで停止要求のチェックを行うことを明確化できます。
using namespace ex = std::execution;
ex::scheduler auto sc = ...;
ex::sender auto async_work =
ex::starts_on(sc, ex::just(10)) |
ex::then([](int n) {
return ...;
}) |
ex::unless_stop_requested |
ex::then([](int n) {
...;
return n;
});
CodeQLにおけるアサーションに対する静的解析の実装経験をP2900の肯定のために使用しない事を推奨する文書。
CodeQLはGithubが開発している静的解析ツールです。CodeQLでは最近C/C++の一部のアサートに対する静的解析機能を実装し、それについてCppConでの講演があったようです(この提案はその講演者の方の一人によって書かれたものです)。そして、そのことがC++26 Contractsをめぐる議論の中で言及されることがあったようで、この文書はCodeQLでの経験は概念実証段階にあるためP2900の静的解析サポートの実現可能性を評価する根拠として使用しないように求めるものです。
文書では、統一的なアサーションがあることによってCodeQLがそれを把握するのに役立つ可能性があるとしつつも、P2900の他の側面によってそれ以外の問題がもたらされるため、静的解析ツールの全体的な動作のために有用であるかはまだ分からないとしています。
P3724R1の紹介スライド。
P3724R1については以前の記事を参照
P3724R1で提案されている様々な丸めモードによる剰余計算の必要性や、標準におけるサポート状況、提案の方向性などについて簡単に紹介されています。
Contracts機能の設計目標について説明した文書。
この文書では、C++26の機能凍結前後で急増したP2900のContracts機能への拒否・修正・再設計などの案への反論として、そもそもP2900が何を解決しようとしていたのかについての設計目標を説明するものです。
この文書によれば、C++における契約機能の導入に関する当初の(そして現在も変わっていない)構想は、関数に対する単一のアノテーションによって2種類の異なる正当性検証ツールを可能にするというものでした
assertマクロの様なオプショナルな実行時検査
- 正当性チェック、静的解析
この実現のためには、これらの機能の既存実装において一般的な使用方法から一部妥協する必要がありました
- アサーションは関数宣言内に記述する必要がある
- アサーションは
bool型に変換可能なC++の式によって記述する必要がある
この構想は、C++の進化におけるもっとも重要なガイドラインの一つである、「新しい機能を追加して言語を複雑化する場合は、それが複数の問題を解決できるようにする必要がある」という原則に基づいたものです。
この意味において、Contracts機能に関する提案は既存の慣行(アサーションと静的解析用アノテーションに関する)を単に標準化するだけではなくそれを次のレベルへと引き上げようとしています。すなわち、オプショナルな実行時検査と静的解析の間に相乗効果をもたらすこと目指しています。これには、実行時検査のみのアプローチでは対処する必要のない新たな課題が伴います。
これによるプログラマのメリットは、アサーションを一度記述するだけでそれに基づいて複数の方法で正当性を検査できるようになることです。
また、P2900までの提案ではその設計において考慮されていなかった3つ目のツールが最近(2024年ごろ)報告されました。それは、ライブラリが明示した事前条件が破られた場合にプログラムを確実に終了させる非無効化保証を提供することです。P2900の設計はこれをサポートするように拡張可能であることは確認されているものの、C++26時点ではサポートされていません。
それ以前の設計目標のモデルは、プログラマが既に確認済みである事項を再確認するものとしてアサーションとその検証を利用していますが、この3つ目のツールはライブラリ作成者がアサーションによって事前条件を指定するものの、違反時の動作まで指定してしまっていることで、アサーションがその関数の動作の一部になってしまう懸念があります。このため、以前の2つの設計目標に適合するかどうか非常にきわどいラインにあります。
この3つ目のツールを重視するために、設計の妥協案として挙げられている「保証された終了を伴うアサーションを提供するだけ」というものは最初の2つの設計目標に向けた一歩ではありません。なぜならそれは単一の目的を達成するためだけのものでしかなく、専用の言語機能を導入しても既存のアサーションマクロ以上のものは得られないからです。
そのためならば、せいぜいチェックと違反時の確実な終了、そしてグローバルな違反ハンドラを提供するためのライブラリ関数があれば十分です。pre post contract_assertは不要です。これはContracts機能ではありません。
3つ目のツールを重視するにしても、最初の2つの設計目標を軽視すべきではありません。
提案されている再設計案にはこれとは別に、P2900 Contractsの持っている機能を細分化して言語機能とし、それを組み合わせてContracts機能を構成しようとするものがあります。この方法ではアサーションの評価についてを細かくユーザーが制御することができますが、一方で契約アサーションを記述する普遍的な表記法が導入されないため、最初の設計目標の1つである静的解析のためのアノテーションの提供ができなくなります。
なお、この提案における静的解析とは、プログラムの入力によってアサーション違反が発生する可能性があるかどうかを判定する解析を指しており、その用途においてアサーションはアノテーションでしかなく、評価セマンティクスは関係ありません。
P3776R1の紹介スライド。
P3776R1については以前の記事を参照
P3776R1の提案内容の紹介と、懸念点の説明がされています。
P3793R0の紹介スライド。
P3793R0については以前の記事を参照
P3793R0で提案されいている整数のビットシフトを行うライブラリ関数のモチベーションや機能性などについて簡単に説明されています。
浮動小数点数演算におけるオーバーフロー時の未定義動作を修正する提案。
CWGの議論の中で、浮動小数点数型の演算中の無限大に関する次のようなことがWell-formedであるかが合意が取れていないことが分かりました
FLT_MAX * 2のような、無限大(inf)を発生させる演算
infinity() + 1のような、無限大を伝播する演算
numeric_limits<T>::infinity()のように、無限大を取得すること
まず、浮動小数点数演算のうち未定義動作となるものについては [expr.pre]/4 で規定されています
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.
[Note: Treatment of division by zero, forming a remainder using a zero divisor, and all floating-point exceptions varies among machines, and is sometimes adjustable by a library function. — end note]
式の評価中に結果が数学的に定義されていない場合、あるいはその型で表現可能な値の範囲外である場合、動作は未定義となる。
[注記:ゼロ除算の扱い、ゼロ除数を用いた剰余演算、およびすべての浮動小数点例外処理はマシンによって異なる場合があり、場合によってはライブラリ関数によって調整可能である。――注記終了]
2つの有限数間の演算はゼロ除算を除いて数学的に定義されていますが、その結果が表現可能な値の範囲に含まれない場合はその動作は未定義となりえます。この表現可能な値の範囲は[basic.fundamental]/13にあります(CWG2723によって最近追加されたようです)。
The minimum range of representable values for a floating-point type is the most negative finite floating-point number representable in that type through the most positive finite floating-point number representable in that type. In addition, if negative infinity is representable in a type, the range of that type is extended to all negative real numbers; likewise, if positive infinity is representable in a type, the range of that type is extended to all positive real numbers.
[Note: Since negative and positive infinity are representable in ISO/IEC/IEEE 60559 formats, all real numbers lie within the range of representable values of a floating-point type adhering to ISO/IEC/IEEE 60559. — end note]
浮動小数点型で表現可能な値の最小範囲とは、その型で表現可能な最も小さい負の有限浮動小数点数から、最も大きい正の有限浮動小数点数までの範囲を指す。さらに、負の無限大がその型で表現可能な場合、その型の表現可能な範囲はすべての負の実数に拡張される。同様に、正の無限大が表現可能な場合、その型の表現可能な範囲はすべての正の実数に拡張される。
[注記:ISO/IEC/IEEE 60559規格に準拠する浮動小数点形式では、負の無限大と正の無限大が表現可能であるため、ISO/IEC/IEEE 60559規格に準拠するすべての浮動小数点型において、実数全体がその表現可能な値の範囲内に含まれる。――注記終了]
翻訳はすべてPLamo翻訳を使用。
この定義の解釈について、表現可能な値の範囲に無限大とNaNが含まれるかどうかによって2つの解釈があるようです。これにより次の3つの相反する見解が存在しています
- 無限大は表現可能な値の範囲に含まれるため、無限大を生成する浮動小数点数演算のオーバーフローはWell-formed
- [expr.pre]/4 で結果が表現可能な値の範囲内にあると述べているのは、丸め処理が行われる前の数学的な結果を指しており、丸め処理によって結果が無限大になる前の状態を指している。実数はすべて表現可能な値の範囲内にあるため、浮動小数点数演算のオーバーフローはWell-formed
- [expr.pre]/4 は、式の評価中に発生するあらゆる種類の結果(無限大も含む)について言及している。実数が表現可能な値の範囲内にあるかもしれないが無限大はそうではないため、無限大が存在する場合でも浮動小数点数演算のオーバーフローは未定義動作。
- [expr.pre]/4 の注記は浮動小数点例外の処理はマシンによって異なると述べており、これは浮動小数点オーバーフローを未定義動作として扱うことの設計根拠を示している
また、CWG2168ではSG6の見解として、無限大を生成する算術演算は定数式では許可されないが、std::numeric_limits<T>::infinity()の使用は問題ない(ただし部分式として使用できない)、というガイダンスを示しています。
CWG2723によって追加された表現可能な値の範囲の定義とこのガイダンス、および[expr.pre]/4の記述は一貫していないようです。この提案は、SG6のガイダンスに準拠しつつCおよび現在constexpr指定済みの<cmath>関数との整合性を保ちながら、合意が得られる最小の変更を行うことによって、この問題を解決しようとするものです。
ここで提案されていることはおおむね、FE_INEXACT例外を除いた浮動小数点例外を発生しない演算のみをWell-definedとして、定数式でも使用できるようにすることで、次のような動作を提案するものです
- 有限値のオペランドから無限大/NaNを生成することはWell-formedだが、定数式では実行できない
- 無限大またはquiet NaNを伝播させる演算はWell-formedであり、定数式で実行可能
- 結果を表すために無限大/NaNが利用できない場合、オーバーフローは未定義動作
GCC15は提案しているこのような振る舞いを実装している唯一の実装であるようです。
冒頭で挙げた例では次のようになります
FLT_MAX * 2はWell-formedだが、定数式では実行できない
infinity() + 1はWell-formedであり、定数式で実行可能
numeric_limits<T>::infinity()はWell-formedであり、定数式で実行可能
この提案における設計変更点は、浮動小数点数演算でオーバーフローが発生した場合、無限大が表現可能な場合は無限大が返されるようになることが規定される(ただし、定数式で実行可能ではない)点のみです。FLT_MAX * 2のようなコードが定数式であると仮定していたコードはコンパイルエラーになります。
↓
↓
std::indirect<T>へ参照型T&への暗黙変換を追加すべきではないとする提案。
std::indirect<T>はC++26で追加されたライブラリ機能で、ヒープ上に確保されたオブジェクトに値セマンティクスを付与するものです。すなわち、std::indirect<T>はポインタを介して対象のオブジェクトを所有し、std::indirect<T>をコピーするとそのオブジェクトもコピーされ、constアクセスはconst性を対象オブジェクトまで伝播させます。
また、std::indirect<T>をムーブするとムーブ元のstd::indirect<T>は空状態になります。C++26仕様では、std::indirect<T>が空ではない事を事前条件としているのは->と*のみであり、このことはstd::unique_ptrやstd::optionalなどと整合しています。
アメリカから提出されたNBコメント(US 77-140)では、std::indirect<T>をT&に変換できるようにすることで使用例を簡素化でき、reference_wrapperに近くなる、ということが提案されています。
この提案はこれに反対するものです。主な理由は次のものです
reference_wrapperに近くすることがなぜ望ましいのかは説明されていない
std::indirect<T>はreference_wrapperよりもstd::unique_ptrやstd::optionalに近いクラス型
- 提案されている暗黙変換演算子は空状態でないという事前条件を持つものの、標準化の最終版で未定義動作の発生しうる状況を増やすことは回避したい
ただし、使用経験と実装経験を十分に積んだ後で再検討することはでき、将来望まれた場合の反対意見ではないとしています。
この提案は2025年11月の全体会議で承認されたようです(ただし何か変更を伴うものではありません)。
std::filesystem::pathのフォーマットにおいて情報の損失が発生しないようにする提案。
C++26 P2845R8でstd::filesystem::pathのフォーマットサポートが追加されています。この提案では、Windowsにおいて実行時コードページではなくリテラルエンコーディングに基づいてフォーマット先のエンコーディングを決定することで、Windowsにおけるエンコーディングの問題に対処しています。
しかし、それも完全ではなく、Windowsにおけるパス文字列としてペアになっていないサロゲート(ペアの片割れ)が現われる場合に正しくエンコードできない問題があります。
auto p1 = std::filesystem::path(L"\xD800");
auto p2 = std::filesystem::path(L"\xD801");
auto s1 = std::format("{}\n", p1);
auto s2 = std::format("{}\n", p2);
p1とp2は異なる文字列ですが同じ文字列(U+FFFD REPLACEMENT CHARACTER)にエンコーディングされてしまっています。この問題はPOSIX環境では起こらないためプラットフォーム間での一貫性がありません。また、これによりpathと文字列の往復変換が不可能になります。
この提案では、このような場合におけるデータ損失を防ぐために、filesystem::pathのフォーマットにおいては不正なUTF-16文字列をWTF-8を使用してフォーマットすることを提案しています。
WTF-8(Wobbly Transformation Format − 8-bit)はUTF-8のスーパーセットであり、16bitコードの任意のシーケンスをUTF-16として不正な場合でも損失無く表すことができます。
auto p1 = std::filesystem::path(L"\xD800");
auto p2 = std::filesystem::path(L"\xD801");
auto s1 = std::format("{}\n", p1);
auto s2 = std::format("{}\n", p2);
このようにフォーマットされた文字列からは、このことを仮定する文字列変換によって再び同じ表現のpathに戻すことができます(この変換は後程提案されるとのことです)。
ただし、WTF-8はユニコード標準の規定するエンコーディングではないため。std::printlnによって出力しても上記の変換後文字列は正しく出力されません。
std::print("{}\n", std::filesystem::path(L"\xD800"));
�
11月に行われたKona会議でWDに適用されたライブラリに対するIssue報告の一覧
11月に行われたKona会議でWDに適用されたライブラリに対するIssue報告の一覧
↑(P3905)との違いは、事前にイシューとして公開されていなかったもののその修正の妥当性が(会議の間に)確認されて準備ができたものをまとめた文書の様です。
std::copyable_functionから::result_typeを削除する提案。
C++17では呼び出し可能型のインターフェースの一部となっていたresult_typeが非推奨化(P0090R0)され、C++20で削除されました。その際、std::functionのみで後方互換のために残していました。
C++23~26でstd::functionのファミリが増えた際、それらのものにもresult_typeが追加されていました。std::function_refにはなかったようですが、ライブラリIssueとして追加することが促されています。
この提案はそれに対して反対し、result_typeのような情報を取得するための専用の型特性を別に追加することを提案しています。
ただし、C++26の作業フェーズは終わっていることもあり、ここで提案しているのはC++26で追加されたstd::copyable_functionからresult_typeを取り除いておくことのみです。これにより、ひとまずresult_typeの使用を抑制しつつ、そのほかの措置を検討することができます。
std::move_only_functionはC++23で追加されたものであるため、ここでは手を付けていません。
この提案がresult_typeの復活に反対している理由は、P0090の主張と同じく、ラムダ式を始めとするCallable型の多くがこれを備えていないことにより、一貫性が無いためです。
ただし、呼び出し可能オブジェクトの戻り値型を取得したい場合というのは確かにあるため、代わりにより一般的に呼び出し可能型の情報にアクセスするための型特性std::signature_traitsとstd::callable_traitsを追加することを検討しています。
std::signature_traitsは関数型を受けて、その関数型のもつ情報にアクセスするためのメンバを備える型特性です。
template<...>
struct signature_traits<R(Args...) cv_qual ref_qual noex> {
using unqualified = R(Args...);
using return_t = R;
template<size_t i>
using param_t = Args...[i];
static constexpr size_t arity = sizeof...(Args);
static constexpr bool is_noexcept = noex;
template<class U>
using cv = U cv_qual;
template<class U>
using ref = U ref_qual;
};
これはあくまで説明のための定義の例であり、細部が正確ではありません。
std::callable_traitsは、呼び出し可能型から関数型を取得してstd::signature_traitsにアクセスする型特性です。
template<class T>
requires is_class_v<T>
struct callable_traits<T> : _callable_traits_impl<^^T::operator()> {};
template<class F>
requires is_function_v<F>
struct callable_traits<F*> : signature_traits<F> {};
template<class S>
struct callable_traits<copyable_function<S>> : signature_traits<S> {};
template<class T>
struct callable_traits<reference_wrapper<T>> : signature_traits<T> {};
このような型特性の方がより広く使用することができ、一貫しています。
<charconv>の浮動小数点数変換をconstexpr対応する提案。
<charconv>にあるto_chars/from_charsのうち、整数型の変換についてはすでにconstexprになっていますが、浮動小数点数の変換はどちらもconstexprではありません。
この提案では、浮動小数点数型のto_chars/from_charsオーバーロードをどちらもconstexprにする提案です。
浮動小数点数型のto_chars/from_charsオーバーロードがconstexprではなかったのは、定数式でそのような変換を実行する実装経験が無かったためです。しかし近年、定数式で実行可能かつ高速な浮動小数点数と文字列の変換アルゴリズムがいくつか実装されています。
浮動小数点数型のto_chars/from_charsオーバーロードをconstexprで実装できることは実証されているため、この提案はそれを提案しています。
C++26 Contractsをホワイトペーパーとして出荷すべきとする提案。
P2900のContracts機能は言語にハードコートされた部分が多すぎることで柔軟性や拡張性に欠けており、C++29スコープのラベル機能を考慮しても言語仕様及び委員会による設計への依存が多すぎることで、ユーザーの多様なニーズに応えられておらず、今後も応えられないと主張しています。
例えば、ユーザーのニーズを満たすために何か変更をくわえようとすると、通常のWG21のプロセスを経る必要があり、これはハードルが上がるとともに時間がかかります。
この提案では、AdaのAssertプラグマを参考にした、シンプルでユーザーが任意に拡張可能な実装方法を概説し、それを検討するためにContractsをホワイトペーパーに移すことを提案するものです。
この提案が異議を唱えているのは主にアサーションのセマンティクス(評価セマンティクスと違反ハンドラの動作)がハードコートされている点です。
この提案では、任意のセマンティクスは次の2つのビルディングブロックを提供することで実装可能だとしています
- ユーザーが設定した評価セマンティクスを問い合わせる機能
- プログラムから契約違反を発生させる方法
これを用いてquick enforceは次のように実装できます
- 述語はユーザーが評価セマンティクスをどう設定しているかを問い合わせることができ、ユーザーがquick enforceを設定していた場合は述語の評価結果が
falseだった場合に直ちにabort()/__builtin_trap()/__fastfail()を呼び出す
- ユーザーが契約違反時に強制終了する事を想定している場合、ここでのP2900との違いは違反ハンドラの呼び出されない点のみ
- ラベル機能によって同等の事が実現される予定であるため、これを実装できない理由はない
ここでの述語とは、契約条件とは異なるものの様です。この述語をユーザーが関数や関数オブジェクトの形で記述して、それを契約アサーションの評価時に使用できるようにすることでカスタマイゼーションポイントを提供し、それを通じてアサーションのセマンティクスを柔軟に制御しようとするものの様です。
この提案によるこのような設計であれば、すべてのそしてそのほかに必要となるアサーションのセマンティクスはライブラリ、あるいはユーザーサイドで実装し提供可能となります。実際に、現在の4つの評価セマンティクスとそこから派生するいくつかの評価セマンティクスについて、すべて標準でハードコートする必要がないことを示しています。
ラベルでもほぼ同様のことが達成できる見込みですが、次のような点で劣っているとしています
- 委員会による設計の枠内に収まっている
- コア言語に組み込まれた機能であり、委員会の想定外の方向に拡張しようとするとコストと時間がかかってしまう
- ラベルによるカスタマイズはアルゴリズムのフックでその動作をカスタマイズするようなもので、アルゴリズムそのものを変更できない
- 例えば、契約条件式からの例外送出を契約違反に変換する/しない
- ラベルを使用するためには複雑で新しい専用のコードを書く必要がある
- この提案の方法は、関数/関数オブジェクトを書くだけでカスタマイズできる
- ラベルは低レベルなビルディングブロックではなく、ラベルの下にあるものを変更できない
P2900を急いでISに移行することで、このような代替案の可能性が閉ざされてしまい、少なくとも著者の方はWG21がその準備ができているとは言えないとしています。P2900は紙の上で設計され実証も展開もほぼされておらず、貴重な代替案も試さずに議論だけで却下しており、実際のユーザーにとって不十分なものになっている、としています。
Contractsの評価セマンティクスを名前マングリングに含めるようにする提案。
C++26 Contracts(P2900)の仕様に対して色々と意義が提出されており、その中の一つに複数の翻訳単位がリンクされる際にすべての翻訳単位で評価セマンティクスを一貫させる方法がない、というものがあります。例えば、mainの翻訳単位をenforceセマンティクスでコンパイルしていても、ignoreセマンティクスでビルド済みのライブラリをリンクする場合にそのライブラリの関数においては契約チェックが行われず、これを検出したり制御する方法がありません。
この提案はこれに対する解決の方法として、契約アサーションが指定されている関数の名前マングリング処理において、選択された評価セマンティクスを含めるようにするという方法を提案しています。
名前マングリングに評価セマンティクスを含めることによって、ある関数に対して評価セマンティクス毎にコンパイルされた関数のコードが同じオブジェクトファイル内で共存可能になります。これによって、インライン展開されない関数であっても、使用場所(呼び出し側)の評価セマンティクス指定に対応した関数が呼び出すことができるようになります。
オブジェクトファイルにおいて4つの評価セマンティクスに対応した関数の実体が含まれていれば、それをリンクする翻訳単位において指定されている評価セマンティクスによってコンパイルされた関数のシンボル名はその4つのいずれかに対応することになります。これによってビルド済みライブラリであっても、主たる翻訳単位の設定に応じた評価セマンティクスの指定が可能になります。
もしリンク先のオブジェクトファイルにおいて指定された評価セマンティクスによるシンボル名が見つからない場合はコンパイルエラーとなりますが、これは評価セマンティクスの指定が混合されている(必ずしも4つ全ての評価セマンティクスによるシンボルが含まれない)場合のビルドにおいてリンカエラーを引き起こすことになり問題となります。
そこで、リンカもContractsの評価セマンティクスに関する知識を持っておくようにすることでこれを解消します。主体となっているオブジェクトにおいて指定されている評価セマンティクスに該当するシンボルを見つけられない場合、リンカは別の評価セマンティクスを選択してリンクを続行できるようにします(このリンク時の評価セマンティクス選択は現在のContracts仕様の範疇です)。その際、リンカはコマンドライン引数として優先順位付きの評価セマンティクスリストを受け取り、このリストの順番で評価セマンティクスを変更して同じ関数のシンボルを探索するようにします。リストをすべて探索してもシンボルが見つからない場合にリンカエラーを発します。
このリンカに渡す評価セマンティクスリストは必ずしも既知の評価セマンティクスのすべてを網羅している必要はなく、例えばignoreセマンティクスだけを除外することで暗黙的にignoreセマンティクスが選択されることを防止することができます。
ここまでの方法だと、インライン関数のアドレス等価性について問題があります。すなわち、インライン関数のアドレスは一つのプログラム内では翻訳単位を跨いで同一である必要があるものの、異なる評価セマンティクスでコンパイルされた翻訳単位ではそのアドレスが異なる場合があります。
inline int f(int n)
pre(n != 0)
post(r: 0 < r)
{
...
contract_assert(n < 100);
...
}
void call() {
auto* pf = f;
}
void call() {
auto* pf = f;
}
このf_enforceとf_observeのシンボル名は契約アサーションの動作が異なるため別の実体となり、翻訳単位AとBの間でpfのアドレスは一致しません。このような状況は現在のC++では未定義動作となります。
この提案ではこの解決のために、コンパイラがこのようなアドレスの取得に対してundefinedシンボル名を発行するようにすることを提案しています。undefinedシンボル名はコンパイラによってこのような場合(契約がなされたインライン関数のアドレス取得時)に発行されるものの対応する実体コードは生成されません。この場合、先ほどのようにリンカは渡された評価セマンティクスリストの一番上にある最も優先度の高いセマンティクスを選択し、そのシンボルのアドレスをundefinedシンボル名のものの代わりに入れるようにします。
void call() {
auto* pf = f;
}
void call() {
auto* pf = f;
}
リンカによるこのような処理によって、インライン関数のアドレスは評価セマンティクスの指定に関わらずすべての翻訳単位で同一に保たれます。
ただしこの処理が行われる場合、評価セマンティクスの選択が再び不透明になります。とはいえ、ユーザーが指定している評価セマンティクスリストの一番優先度の高いものが選択されることになるので、結果として選択されたセマンティクスはその翻訳単位ビルド時に指定されていた評価セマンティクスと同等かより厳密なものになるはずです。
このような方法によって、インライン関数がインライン展開されているかに関わらず同じ評価セマンティクスで評価されることや特定の評価セマンティクスをプログラム中で厳密に指定することなどが達成でき、P2900仕様に対して提出されている意義のうち1つが解消されます。
ビルド済みライブラリを配布する場合、特定の一つの評価セマンティクスに対応するシンボルだけを生成しておくのではなく、既知のすべての評価セマンティクスに対応するシンボルをすべて含めるようにして配布しておくと、利用者の指定に応じた評価セマンティクスの選択が可能になります。これは、CUDAのfat binaryとしてすでに実用化されているアプローチと同等です。
このアプローチをとる場合でもまだ問題はあり、extern "C"関数やmain()はこのような評価セマンティクスに応じたシンボル生成を行うと必ず多重定義エラーが発生する事です(似た問題がグローバル変数の初期化においてもあります)。特にmain()の場合は実体が一つしか存在できないことで、そのmain()をコンパイルした評価セマンティクス指定によって実行ファイル全体の評価セマンティクス指定が決まってしまいます。これにより、プログラムで局所的に評価セマンティクスの指定を変更するようなことが妨げられます。
この提案ではこの解決としていくつかの方法を提示しています
- これらの特殊なエンティティに対して複数のシンボル生成を抑制するオプションを追加する
- そのソースファイルをコンパイルする評価セマンティクスのリストを指定するオプションをコンパイラに追加する
- コンパイラはコンパイルの際、そのリストにある評価セマンティクスに応じたシンボルを生成する
- が、
extern "C"関数やmain()などはリストの先頭のセマンティクスに対してのみコードを生成する
ただしどちらの方法でもmain()関数をコンパイルする際の評価セマンティクス指定がプログラム全体に影響してしまう問題を回避できません。この対策として関数呼び出し時に評価セマンティクスを指定する方法を提案していますが、構文等の設計に議論が必要なためそれは将来の標準化における課題としています。ここでは暫定措置として、evaluation_semantic_cast<Semantics>()による明示的な構文を提案しています。
void myFunction();
void callMF() {
myFunction();
evaluation_semantic_cast<enforce>(myFunction)();
evaluation_semantic_cast<undefined>(myFunction)();
}
提案ではさらに、仮想関数の場合のアプローチやモジュールの場合、可能な最適化などについて検討されており、筆者の方のコードベースにおけるP2900の現状の問題の程度の報告などがされています。
この方法を実現するためには、コンパイラだけではなくリンカにも変更が必要となります。C++標準規格としてはリンカの存在を特に取り扱ってはいないものの、リンカに対する影響については慎重なところがあります。この提案では、リンカへの影響を気にするあまり言語機能の進化を妨げた例が過去にあり、この問題についても同様(このままだと同様になる)としています。
契約アサーションの評価セマンティクスから、ignoreの選択を制御できるようにする提案。
契約アサーションの評価セマンティクスが集中管理されていることによって、ビルド構成やプロジェクトポリシーによって容易にignoreセマンティクスに設定されてしまう可能性があるため、Contracts機能を信頼性・モジュール性の低いものにしてしまうとして、この提案では無視されないように制御できるようにすることを提案しています。
ここでは次の4つの案を提示しています
pre!の様な構文によって、必ず終了セマンティクスで評価されるアサーションを指定できるようにする
- ignoreセマンティクスを削除する
- P3400で提案されている完全なプロパティシステム(ラベル指定)を採用する
- P2900 Contracts MVPを別の方法で出荷する
- 1~3のいずれも合意に至らない場合
- C++29, white paper, TS
この提案では1のpre!を推奨しています。
この提案はルーマニアのNBによるNBコメントと連動した提案の様です。
常に評価され破られたら終了する契約アサーションの設計上の疑問点をまとめた文書。
ルーマニアNBのコメントおよびそれを受けてのP3911R0(上の方に解説があります)では、通常のセマンティクス可変なアサーションに対して、常に評価され条件式がfalseになったらプログラムを終了するような契約アサーションをC++26に向けて導入しようとしています。提案ではpre!(expr)のように通常のアサーションに対して!を後ろに付けた構文を提案しています。
EWGはこのNBコメントと提案を受けて、C++26に向けてこのようなアサーション(always enforced assertion)の追加を検討することにしたようです。
この文書では、NBコメント解決のためにこの段階で新しい機能を急いで導入することはWG21の標準化モデルに反しており、C++26WDにおけるContracts機能はこの様なアサーションが無いことを理由に遅延させるべきではないとしています。
しかし、そのうえでEWGがこのような機能を検討するならば、設計空間を慎重に検討し設計選択におけるトレードオフを十分に理解することが重要であるとして、この文書はalways enforced assertionの設計に関しての提言をするものです。
always enforced assertionはC++29で予定されているContractsの機能拡張であるラベル機能(P3400R2)とかなり重複があることが分かっています。これはC++26に導入される予定はなくC++29に対してもまだ検討中ではありますが、SG21において複数回のレビューを受けており、always enforced assertionの様な機能を包含する契約アサーションの拡張機能として現時点で最も検討が進んでいる設計基盤となっています。
この文書ではラベル機能の設計の経験をベースとして、always enforced assertionおよび類似の機能提案が回答する必要のある設計上の疑問点を提示します。
- 構文
- 既存キーワードに記号を付加する構文にすべきか?その場合どの記号を使用すべきか?
!は慣習的に否定を意味するため、認識上の混乱の可能性がある
pre!の様な構文は、rustの宣言マクロのような機能のために確保しておいた方が良い可能性がある
- 今のところ、他の記号について代替案はない
- 現在の契約構文はCとの互換性もある程度考慮されている
- 関数マクロで再現可能な構造になっていることで、C側で無効化できる
- デフォルトの構文(
pre(), post(), contract_assert())はどちらのアサーションを表すべきか?
- always enforced assertionにデフォルトの構文を割り当てて、無視可能(セマンティクス可変)なアサーションに別の構文を当てるべきという提案があった
- また、デフォルトを決定できないのならば現在の構文を廃止して、セマンティクス可変/固定アサーションそれぞれに別の構文を与えるべきという案もあった
- どちらのアプローチでもC++26 Contracts機能の大幅な再設計となり、P2900R14が強い合意を得てWDにマージされ、現在NBコメントフェーズであることを鑑みると、このような変更はWG21標準化における慣行に反する
- always enforced assertionと通常のアサーションの構文は、どれほどの関連性を持つべきか?
- 構文の長さ、構文構造の類似性など
- どちらのアサーションがデフォルトになるかに関係なく、always enforced assertionと他のアサーションの構文がどのように関連付けられるかを明確にする必要がある
- 同じキーワードのわずかな違いで関連性を明示すべきか?
- 別のキーワードを利用して異なる機能であることを明確化すべきか?
- 片方の種類のアサーションは他方よりも短いことが望ましいか?
- など
- always enforced assertionの構文はラベル機能との前方互換性を持つべきか?
- always enforced assertionがC++26で導入され、ラベル機能がC++29で導入されると、契約アサーションにおいて全く同じことをするために2つの方法が存在することになる
- この状況を回避する方法の一つはラベル機能に対する前方互換性を持たせることだが、その場合はその構文の仕様が
<contracts>ヘッダに依存するかなどの問題に対処する必要がある
- どのような選択を取ったとしてもラベル機能の設計空間を侵食するため、これらの選択の適切な決定のためにはP3400R2をC++26のスコープ内でEWGにおいてレビューする必要があるが、NBコメントフェーズでそれは実現可能性が低い
- セマンティクス
- always enforced assertionはenforce/quick_enforceのどちらのセマンティクスを採用すべきか?
- enforce/quick_enforceセマンティクスはどちらも、
pre!()の様なアサーションの要件に合致している
- 単一の構文で両方のセマンティクスを許容する場合、その選択は結局コンパイラオプションによって行われる必要がある
- このようなオプションは通常のアサーションのオプションとは区別されている必要がある
- このことはalways enforced assertionの動機と矛盾する可能性がある
- 新しい構文はどのアサーションに対して利用可能になるべきか?
pre, post, contract_assertのいずれか一部、あるいは全部?
pre, postとcontract_assertでは考慮すべき設計上の問題が異なるため、この選択が重要になる
- 2種類のアサーションの混在は許可されるか?
pre!(x)とpre(y)を1つの関数宣言で共存させることができる場合、その評価順序は保証すべきか?
- always enforced assertionの動機の一つである、事前条件違反ごにすぐ終了することによる利点を活かすためには、その評価順序を保証する必要があるが、この選択はalways enforced assertionの設計を大幅に制約することになる
- 関数が間接的に(他の言語から)呼ばれる場合でも、
pre!/post!は評価されるべきか?
- always enforced assertionの目的から、関数ポインタを介した関数呼び出し時にも
pre!/post!を評価することには異論の余地がない
- このような評価を行うには呼び出し先側で
pre!/post!を評価する必要がある
- このような場合呼び出し元は契約アサーションを認識できないが、契約アサーションは関数型の一部ではなく関数ポインタは契約アサーションを保持できない
- このため、コンパイラは呼び出し元側でチェックを行うことができなくなる
- この要件はalways enforced assertionとその類似提案が仕様化可能かつ実装可能であるために順守しなければならない追加の設計制約を課す
pre!()の条件式評価規則はpre()と同じになるべきか異なるべきか?異なるとしたらどう異なるべきか?
const化、副作用の省略、重複評価、例外の扱いなど
pre!()がC++26WDの最小限の拡張として設計される場合、同じ規則に従うべき
- 一方、always enforced assertionの動機と要件から、これは通常の契約アサーションとは根本的に異なる機能であり、P2900/P3400と独立して設計された場合異なる規則に従うことになることが予想される
- また、契約とプログラムの正当性の関係についての見方によっては、always enforced assertionは決定論的なプログラムの動作の一部としてみなされる可能性がある
- P2900の契約チェックがゴーストコードと位置付けられているのは、P2900の中核原則の一つである「プログラムの正しさを検証するためのチェックを追加しても、その正しさを変更してはならない」に従う物
- P2900の条件式評価に関する批判されがちなルールはこれに従っている
- 2種類のアサーションが異なる評価規則に従う場合、両者が同じセマンティクスで評価されたとしてもその条件式の意味は異なることになる
- always enforced assertionは実装済みか?そのABIへの影響はどのようなものがあるか?
- P2900は関数の宣言に契約アサーションを追加してもABIの変更の必要がなく展開可能であることを意図して設計されており、2つの主要なコンパイラで実装されている
- そうでなければ、ツールチェーンのアップデートが必要となり、展開が大きく遅れることになる
- 同じアサーションの枠組みに載っている以上、always enforced assertionにも同じ基準を適用するのが妥当
- 2.5の問に関して、異なる評価規則を採用する場合はなおさら
- 実装可能性を検証し、ABIへの影響について議論する必要がある
- always enforced assertionが正確に一回だけの評価を保証する場合、ABIの変更を回避するためには呼び出し先側でアサーションのチェックを行わなくてはならない
- これは間接呼び出しをサポートするため
- その場合、通常の契約アサーションとの混在を許可すると、通常の契約アサーションにも同じ制限が適用される
- 通常の契約アサーションは呼び出し元と呼び出し先の両方での評価を許可しているため、これは大きな設計変更となる
- 契約アサーションに仮想関数サポートを追加する場合、always enforced assertionはそこにどのように組み込まれるべきか?
- 契約アサーションの仮想関数サポートはC++26には間に合わなかったものの、CWGによる文言レビューを通過した経験とGCCによる実装経験があり、C++29の早い段階で導入されると予想される
- always enforced assertionはそこにどのように組み込まれるかを説明する必要がある
- ラベル機能も含めたContractsのC++29の拡張は、相互互換性を確保するために同時に設計されている
- always enforced assertionはそうではないため、これらの機能に対する先方互換性を考慮する必要がある
- 評価規則が異なる場合、仮想関数サポートについて困難が生じる
- 基底クラスの契約に対して派生クラスの契約が異なっている場合、正確に一回だけの評価を保証するとこの場合の契約チェックの必要性と矛盾が生じる
- ライブラリ
<contracts>のライブラリAPIを拡張して通常の契約違反とalways enforced assertionの契約違反を区別すべきか?どのように区別すべきか?
contract_violationオブジェクトから契約違反に関する情報を取得できるが、always enforced assertionにおいてこのAPIの振る舞いをどのように変更するか、あるいはしないかを決定する必要がある
- ラベル機能において
contract_violationオブジェクトからラベルを取得する方法はかなり複雑で、実質ラベルが一種類しかない場合にこのAPIを選択するのは疑問がある一方、別の方法を取れば恒久的な設計上の矛盾が生じる
- 標準ライブラリにおける堅牢化された事前条件は、
pre!()で表現できるべきか?
- C++26の契約アサーションは標準ライブラリの堅牢化モードにおけるフレームワークとして利用されているにもかかわらず、C++26の機能セットではそれを実装できない点で批判されている
- 現在は契約アサーションと同等の振る舞いをするベンダー固有のマクロが使用されている
- always enforced assertionがあれば堅牢化された事前条件を実装できるのか?、そうすべきか?
- しかし、堅牢化された事前条件はalways enforced assertionとは根本的に異なるもの
- 堅牢化された事前条件はコンパイラオプションによって評価セマンティクスが変更できる(ignore or enforce)必要がある一方で、always enforced assertionは評価されないセマンティクスが選択されないものであるため
2026/03に行われた全体会議において、Contracts機能にこれ以上の変更を加えずにC++26に導入することが決定されたようです。これによっておそらく、always enforced assertionはラベル機能によって実現されることになると思われます。
↓
Rangeアダプタにおいてstd::optionalを認識し動作を最適化する提案。
C++26ではstd::optionalはrangeになり、Rangeアダプタで使用できるようになります。
auto f() -> std::optional<int>;
int main() {
for (const auto& v : f() | std::views::as_const | std::views::reverse) {
std::println("{}", v);
}
}
しかし、Rangeアダプタはstd::optionalを認識しないため(ほかのrangeと同様に扱うため)その特性を考慮した動作の最適化などを行いません。
std::optionalは範囲としては要素0か1の範囲であるため、例えば上記例のようにviews::reverseする場合は何もしないで返すことができます。しかし現在はそうなっておらず、reverse_viewを構築してreverse_iteratorを返すことになります。
この提案は、Rangeアダプタでstd::optionalを認識して特別扱いすることで、その特性に合わせたアダプタ動作の最適化を組み込もうとするものです。
ここではstd::optional<T>型のoptの入力に対して、次のような変更を提案しています
opt | views::as_const
std::optional<T>、または
std::optional<const U&>(TがU&の場合)
views::take(opt, n)
n == 0: 空のstd::optional
- それ以外:
opt
views::drop(opt, n)
0 < n: 空のstd::optional
n == 0: opt
views::reverse: opt
これ以外のアダプタについては変更を提案していません。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
C++26 CDに対して寄せられたNBコメントの一部の解決のための文言を提供する文書。
ここでは以下のNBコメントの解決のための文言が提供されています
- US 160-260
ranges::unique、ranges::unique_copyについて、入力範囲が空である場合が許可されていない
- US 209-332
senderの指定で使用されているunspecified-exceptionはその仕様が不十分
std::exceptionから派生(曖昧さの無い公開継承)したものであり、dependent_sender_errorとは関係ないことを明確化する
- US 228-348
execution::spawn_futureの規定に登場する説明専用エンティティssource-tはデフォルト構築可能である必要があり、デフォルト構築されたオブジェクトは停止状態と関連付けられるべき
- US 263-396
execution::query_parallel_scheduler_backend()の戻り値のshared_ptrはオブジェクトの所有権を持つべき
- “implements the interface”という表現はC++的ではないため、 “whose type is derived from”に変更する
- US 265-398
parallel_scheduler_backendの3つのメンバ関数の事前条件について、ストレージ(記憶域)についてlifetimeという用語を使用しているが、ストレージには持続期間(duration)がありlifetimeはない
- ストレージについては、lifetimeではなくdurationを使用する
- US 266-399
parallel_scheduler_backendの3つのメンバ関数の事前条件について、“one of the expressions below”は曖昧であるため、“the call to set_value, set_error, or set_done on r”に変更する
- US 112-172
std::meta::extract()で使用されるextract-value()の要件について、派生型から基底型への変換を禁止すべき
Uが配列型である場合にremove_extent_t<U>*のsimilarな型であることを追加する
- US 130-193
- 型のプロパティをクエリするリフレクションメタ関数の規定について、対応する型特性メタ関数が存在していない場合に例外を送出するようにする
- 対応する型特性の使用要件の違反の仕方に応じてどのようなエラーとなるかを規定する
これらの問題は何事もなければC++26でそのまま解消されます。
US 160-260はおそらく並行Rangeアルゴリズムに関連するNBコメントだと思われますが、以前のアルゴリズムの規定(C++20 Rangeアルゴリズムおよびそれ以前のもの)に対しても変更しています。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
C++26 トリビアルリロケーションに対するNBコメントに対応する文書。
この文書は、C++26 CDに対して寄せられたNBコメントのうちトリビアルリロケーション(P2786R13)に関連するものに対して、P2786の著者の方が応答するものです。
寄せられたNBコメントは次の7つに大別されます
- P2786のほぼ全てを削除する
- トリビアルリロケーションと
memcpyを同等にする
std::relocateをstd::uninitialized_relocate_*に置き換える
- 暗黙的なトリビアルリロケーションの修正
- 暗黙的にムーブ操作が
deleteされる場合でも、トリビアルリロケーション可能にする
- 条件付きトリビアルリロケーション
_if_eligibleをキーワードにする
_if_eligibleがbool値を取るようにする
- リロケーション関数において
const型を許可する
restart_lifetime(P3858)
これらのカテゴリごとに、NBコメントに対して返答(賛成・反対・中立、その根拠など)をしています。
概ね次のような姿勢です
- 反対
- 反対
- 反対
- 賛成
- 中立、ただしC++26では現在で十分
- 中立
- C++26に対しては反対
P3858R1の紹介スライド。
P3858については以前の記事を参照
このスライドではP3858で提案されているrestart_lifetime()のモチベーションやユースケース等の説明が簡潔になされています。また、restart_lifetime()のARM64eプラットフォームにおける悪用の懸念やC++26に入れる際の利点欠点なども説明されています。
終了セマンティクスが保証される契約アサーションのニーズについて報告する文書。
C++26のContracts仕様(P2900)では、契約アサーションの評価セマンティクスは固定ではありません。これに対して、「常にチェックされ、契約違反は終了することが保証される」セマンティクス(というよりもそういう動作をするアサーション)のニーズがあり、この文書はそれについて説明するものです。
文書によるとニーズとは次のようなものです
- メモリ安全性を保証するためのアサーションでは、条件は常にチェックされ、条件が
falseなら後続のコードを実行しないために終了しなければならない
- そのようなアサーションでは、条件式の評価は正確に一回である必要がある
- 実行時のオーバーヘッドを最小化し、パフォーマンスを理由に利用を控えることが無いようにする
- 正確に一回だけの評価によって、副作用も制御可能になる
- 関数ポインタなど、間接的な呼び出しにおいても必ずチェックされなければならない
- 条件式の評価に伴う例外送出を契約違反に変換しない
- 変換にはコストがかかり、メモリ安全性アサーションは可能な限り低コストである必要があるため
const化は有害
- メモリ安全機能においては条件式の理解しやすさが重要であり、目に見えない変換の理解を必要とすべきではない
- 必ず一度だけ評価されることで、副作用の懸念は無くなる(副作用は必ず一度だけ発生する)
これらはP2900の提供する契約アサーションでは完全に満たすことができないニーズであるため、実質的に現在のContracts仕様に反対を表明する文書になっています。
トリビアルリロケーションをC++26から削除するための文言を提供する文書。
2025年11月に行われたkona会議において、トリビアルリロケーション(P2786R13)をC++26から削除してC++29をターゲットとして検討しなおすことがEWG/LEWGで合意されたようです。
この文書は、P2786R13の変更をC++26 CDから取り除くための文言の変更をまとめたものです。単純にrevertするのではなく、一部で行われた文言の改善を維持するようにしています。ただし、トリビアルリロケーションに関する機能で残るものはないようです。
2025年11月に行われたKona会議でWDに適用されたコア言語に対するIssue報告の一覧。
量が多いのは、C++26のNBコメント由来のイシュー解決が含まれているためです。
↓
simd::vecの推論補助を復元する提案。
スカラ型の演算コードではブール値を整数型に変換することがたまに行われます。
template<typename T>
void f(Tx, Ty) {
int b = x < y;
}
これはsimd::vecでも行うことができます
template<typename T>
void f(Tx, Ty) {
vec<int> b = x < y;
}
ただし、ここでのbの型は正しくはありません。これはbasic_mask型が得られています。
この場合に単項+を使用することで整数型のsimd::vecに変換することができます。
template<typename T>
void f(Tx, Ty) {
auto b = +(x < y);
}
ただし次のように書けた方がより直感的と思われます
template<typename T>
void f(Tx, Ty) {
basic_vec b = x < y;
}
この提案はこのように書くことができるようにするために、simd::vecにsimd::basic_maskからのテンプレートパラメータ推論を行う推論補助を追加することを提案するものです。
simd::basic_mask<Bytes, ABI>とsimd::vec<T, ABI>のように両者は1つ目のテンプレートパラメータが異なり、ABIタグ型(2つ目のテンプレートパラメータ)の選択には実装の自由度があります。それによって、simd::basic_maskオブジェクトに対する単項+の結果型をユーザーが予想すること(特に2つのテンプレートパラメータが何になるか)は困難になっています。
basic_vec b = x < y;のように書いたときにsimd::vecの2つのテンプレートパラメータを推論するには推論補助が必要となりますが(左辺のbasic_maskに対して+を適用した結果型を取得するようにするもの)、これは著者の方の参照実装には実装されていたものの仕様に対する提案はもれていたようです。この提案ではそのCTADを追加することでsimd::vecの比較演算結果を直接simd::vecで受けられるようにしています。
C++26 CDに対して寄せられたNBコメントの一部の解決のための文言を提供する文書。
ここでは以下のNBコメントの解決のための文言が提供されています
- AT 7-213
std::complexはtuple-likeとなったが、タプルサポートライブラリにおいてそれが明記されていない
- US 140-233
std::hiveの要素列(rangeあるいはイテレータ範囲など)を指定できるコンストラクタ/代入演算子のうち、その操作の後の要素の順序を指定していないものが多い
- US 141-235
std::hive::reserve()について、要素の順序の維持が明記されていない
- 既存要素については何も起こらないこと明確にするために、変更されないことについての言及を削除(サイズも要素の順序も変化することは無い)
- US 145-234
std::hive::trim_capacity(size_type n)について、呼び出し前にキャパシティがn未満である場合の動作が明記されていない
- キャパシティが
n未満である場合、何も起こらない事を明記する
- US 147-240
std::hive::splice()について、例外送出条件がsplice操作の前にチェックされるのか最中にチェックされるのか不明なことによって、例外送出時のコンテナの状態が不明になっている
splice操作の前にチェックされることを明記し、強い例外保証を提供する(つまりコンテナの状態は変更されない)
- US 164-203
- 集合操作アルゴリズムの指定で出現する
Mの定義について、複数の同値要素の処理を考慮しているかが明確ではない
- 効果の指定では、
M > Nの場合にどの要素がコピーされるのかが明確ではない
- US 126-189
type_orderとtype_order_vは不完全型を許容すべき
- US 227-346
- US 229-347
spawn_future(sndr, token, env)の説明で使用されているsの型が不明確
- US 221-339
execution::bulk等の指定に使用されている説明専用check-typesの位置や定義が間違っている
- US 225-341
std::tupleのコンストラクタは条件付きnoexceptではなく、“potentially-throwing”というチェックが正しく機能しない可能性がある
これらの問題は何事もなければC++26でそのまま解消されます。
US 160-260(おそらく並行Rangeアルゴリズムに関連するNBコメント)、US 225-341は以前からあるものに対する変更です。
この提案は2025年11月の全体会議で承認され、C++26に採択されています。
規格文書内での宣言に対するスタイルを一貫させる提案。
C++規格書内では宣言という用語について、類似した2つの使用法が定義されています
- [basic.pre]で定義されている通常フォントの宣言(declaration)
- [dcl]で定義されている文法的な宣言(declaration)
前者は単に宣言という言葉として文章中で使用され、後者は特定の宣言の文法要素を指定するために使用されています。これら2つは規格文書中で適用されているスタイルが異なっています(通常フォントと斜体)。
しかし規格文書中ではこの2つのスタイルが誤って適用されている箇所があるようで、この提案はそれを指摘するNBコメントを受けて再度見直しと調整を行ったものです。
このことからわかるように、この提案はeditorialなもので動作の変更等を伴うものではありません。
std::simdの==の振る舞いについて修正の提案。
この提案は以前のP2892のフォローアップとなる提案です。基本的なところは以前の記事を参照してください
std::simd(std::simd::vec)のoperator==は要素ごとの比較の結果によるマスク(std::simd::basic_mask)を返し、要素列が一致しているかいないかをbool値で返すもの(std::equalやコンテナの比較)ではありません。
この提案はそれを既存のコンテナ等と同じ振る舞い(std::equalによる比較によるような)になるように修正しようとするものです。
この提案ではSIMDの専門家(現状の動作が自然)と一般的なC++開発者(bool値を返すほうが自然)を区別して、後者のユーザーに向けた設計とすることを求めています(P2892では技術的な議論のみが行われていた)。具体的な数値は記載されていないものの、一定の規模のアンケートを行った結果として現在の動作が非直感的だという調査結果も報告しています。
この提案では次の3つの設計案を提示しています
- 既存APIのリネーム
operator==を削除し、現在の比較動作は明示的なフリー関数(equal_elements())で行われるようにする
operator==がboolを返すようにする
std::simdを次のバージョンまで延期する
提案としては1か2の採用を推奨しています。
この提案はルーマニアのNBとしてのNBコメントに基づく提案でもあります。
std::indirect<T>を暗黙的にT&に変換できるようにすることについての解説を行うスライド。
これは、P3902R2で反対意見が出されている問題について、賛成意見を解説するものです。
そのサイズがコンパイル時に判明している場合のsized_rangeを表すコンセプトの提案。
sized_rangeコンセプトではそのサイズはranges::size(r)によって取得されますが、これが定数式で実行可能であるかどうかを調べる方法が無かったため、コンパイル時にサイズが確定しているかどうかを調べてコンセプト定義に反映することができませんでした。
P2280R4の採択により、コンセプトの定義時やSFINAEの文脈においてranges::size(r)の呼び出しが定数式であるかどうかを自然に調べることができるようになりました。そして、C++26のstd::simdではこのテクニックを用いてranges::size(r)がコンパイル時に取得可能かどうかを積極的にチェックしています。
範囲のサイズ取得が定数式で行えるかどうかを判定することが有益なのはstd::simdだけではなく、この提案はrangeの性質としてそれを表現し判定できるようにするためのコンセプトranges::static_sized_rangeを提案しています。
static_sized_rangeはC++26のstd::cw(std::constant_wrapper)を用いて次のように定義できます
namespace std::ranges {
template<class T>
concept static_sized_range =
sized_range<T> && requires(T& t) { cw<ranges::size(t)>; };
}
cw<ranges::size(t)>はranges::size(t)の呼び出しが定数式で行えない場合にエラーとなるため(std::cw<V>がNTTPVによってstd::constant_wrapper<V>を返すため)、Tのオブジェクトtに対してranges::size(t)が定数式で呼び出せない場合にこのコンセプトはfalseになります。
また、static_sized_rangeに対してそのサイズをコンパイル時に取得するための変数テンプレート、range_static_size_vも提案しています
namespace std::ranges {
template<static_sized_range T>
constexpr auto range_static_size_v =
decltype([](T& t) { return cw<ranges::size(t)>; }(declval<T&>()))::value;
}
std::cw<V>はstd::constant_wrapper<V>型オブジェクトを返し、std::constant_wrapper<V>::valueからVを取得できます。この定義ではそれを利用しています。
提案文書より、サンプルコード
static_assert(ranges::static_sized_range<array<int, 3>>);
static_assert(ranges::range_static_size_v<array<int, 3>> == 3);
static_assert(ranges::static_sized_range<span<int, 5>>);
static_assert(ranges::range_static_size_v<span<int, 5>> == 5);
static_assert(ranges::static_sized_range<ranges::single_view<int>>);
static_assert(ranges::range_static_size_v<ranges::single_view<int>> == 1);
static_assert(ranges::static_sized_range<ranges::empty_view<int>>);
static_assert(ranges::range_static_size_v<ranges::empty_view<int>> == 0);
static_assert(!ranges::static_sized_range<span<int>>);
static_assert(!ranges::static_sized_range<vector<int>>);
static_assert(!ranges::static_sized_range<optional<int>>);
static_assert(ranges::range_static_size_v<string> == 42);
さらに、既存のRangeアダプタ等でもこれらのものを活用することを提案しています
integer-class型
range_static_size_v<R>で取得した値の整数型が正しく定数式で使用可能になるように明確化
ref_view
static_sized_rangeをラップする際にその性質を継承するようにする
ref_view<array<int, 42>>のような場合にstatic_sized_rangeとなるようにする
array/span/view_interfaceの.front()/.back()
join_view
- 現在常に
sized_rangeではないが、外側の範囲がsized_rangeであり、内側の範囲が固定サイズである場合などの特定のケースではsized_rangeになることができる
- 内側の範囲が
static_sized_rangeである場合、join_viewをsized_rangeにできる
- 外側も
static_sized_rangeならば、join_viewもstatic_sized_rangeになる
lazy_split_view
tiny-rangeをstatic_sized_rangeで置き換えることで、input_rangeを分割する際のパターンとしてstatic_sized_rangeを使用できるようになる
span
- 範囲コンストラクタについて
- 静的にサイズが決まる範囲から構築する際、サイズチェックをコンパイル時にチェックできる(現在は実行時
- CTADにおいて入力範囲の静的サイズを取得して固定サイズ
spanを推論できる
std::simd/std::inplace_vector
ranges::size(r)が定数式である場合、という条件をstatic_sized_rangeで置き換える
ranges::min/max/minmax
- 入力範囲が空でないという事前条件をコンパイル時にチェックできるようになる
このような広い範囲での応用可能性が挙げられていることが、static_sized_rangeの必要性と有用性を示してもいます。
std::function_refの二重ラップを防止するために、変換コンストラクタをexplicitにする提案。
C++26で追加されたstd::function_refはcallableなものへの軽量な参照となるstd:functionのファミリの一つです。std::function_refはstd::functionなどと同じく指定された関数型と互換性のあるcallableの参照となることができるため、型が完全に一致してコピーコンストラクタが呼ばれる場合を除いて、std::function_refのstd::function_refというものを構築することができます。
しかし、std::function_refはcallableへの参照でありそれを所有していないため、これは参照の参照のようなものであり、ダングリング参照を容易に生成しえる危険な構築経路となります。標準ライブラリで参照セマンティクスを持つ既存の型(std::string_viewやstd::span)はこのような動作をしません。
void call_me_later(std::function_ref<void()> f);
void f();
int g();
call_me_later(f);
call_me_later(g);
call_me_later(std::function_ref(f));
call_me_later(std::function_ref(g));
call_me_later()が受け取った関数を保存していてこの呼び出しの後で使用する場合、寿命が尽きたstd::function_refを呼び出してしまいます。
実際にはこのように構築することはそうそうないはずですが、callableを別の場所から取得してくる場合などでその型が不明瞭な場合、気づかない間にこのような渡し方をしてしまう可能性があります。
auto return_g_ptr() { return g; }
auto return_g_ref() { return std::function_ref(g); }
call_me_later(return_g_ptr());
call_me_later(return_g_ref());
あるいはメンバ関数をstd::function_refに渡したい場合
struct foo{
void f();
int g();
};
foo obj;
call_me_later({std::constant_arg<&foo::f>, obj});
call_me_later({std::constant_arg<&foo::g>, obj});
内実を知らなければconstant_argを用いたこの初期化式は何をしているのか全く明確ではないため、std::function_refを明示的にしたくなるかもしれません
call_me_later(std::function_ref(std::constant_arg<&foo::f>, obj));
call_me_later(std::function_ref(std::constant_arg<&foo::g>, obj));
こうすると同じ問題にぶつかります。これは、後者の場合はstd::function_ref{...}としていることによってまずここでstd::function_refの構築が行われ、CTADによってテンプレートパラメータが推論されます。それはfoo::gに基づいて決定されるため、std::function_ref<int()>になります。一方、call_me_later()の引数型はstd::function_ref<void()>になるので変換が走ります。
前者の方は、引数型std::function_ref<void()>の構築を行うものであるため、CTADは起こらず型変換も発生しません。
このような寿命の問題がない場合でも、二重std::function_refは参照の参照であるため、呼び出しに際して2回の間接呼び出しが必要になります。
この提案は、このような二重std::function_refが暗黙的に発生することを防止するために、テンプレートパラメータが一致しない場合の変換コンストラクタをexplicitにしておくものです。
ここで関連するstd::function_refのコンストラクタは2つあります。
namespace std {
template<typename R, typename... Args>
class function_ref<R(Args...) cv noex> {
...
constexpr function_ref(const function_ref&) noexcept = default;
template<class F>
constexpr function_ref(F&& f) noexcept;
...
};
}
片方はコピーコンストラクタであり、これはテンプレートパラメータがcv修飾やnoexceptまで含めて完全一致している場合にのみ呼ばれるものであり、ダングリング参照を生成しないので問題ありません。問題なのはもう一つの汎用の任意のF&&から構築するコンストラクタで、現状これはテンプレートパラメータが異なるものの呼び出しについて互換性のあるstd::function_refを暗黙的に受け入れています。
この提案ではこのコンストラクタを条件付きでexplicitにすることを提案しています。これにより、先ほどのcall_me_later()で問題となる例は暗黙変換が禁止されることによってコンパイルエラーになるようになります。
提案されているexplicitの条件は簡易に記述することが難しいものの
Fがstd::function_refの特殊化であり
Fの関数型のシグネチャに互換性がない
- 互換性がある場合: 引数型と戻り値型が同じで
noexceptとCV修飾に互換性がある場合
場合にexplicit(true)になります。
また同時に、同じ判定方法で関数型のシグネチャに互換性がある場合に次の動作変更も提案しています
- 該当のコンストラクタを用いて構築された場合、渡された
std::function_refを参照するのではなく、それが参照しているcallableを直接参照するようにする
- 二重
std::function_refを回避する
- この場合は
explicit(false)(暗黙変換可能)
- 代入演算子を有効化する
- 上記の変換コンストラクタを経由して代入されるため、ダングリングや二重
std::function_refにならない
これらの変更により、二重std::function_refが暗黙的に構築されなくなり、可能な場合は二重std::function_refを回避する最適化が行われた上で暗黙変換が許可されるようになります。
この提案はリジェクトされているようです。
標準ライブラリ内のユーザー定義リテラルをすべてconsteval関数にする提案。
実行時の依存関係(実行時にしか取得できない引数など)を持たず、単にオブジェクトを生成するだけの関数は、暗黙的な外部依存関係(動的メモリ確保の必要性など)がない限りconsteval関数であるべきだとして、ちょうどそれらの条件に該当する関数群である標準ライブラリ内のユーザー定義リテラル(operator"")についてconsteval関数に変更しようとする提案です。
ただし、ユーザー定義リテラルのうちでも動的メモリ確保の必要性があるもの、すなわちstd::stringを生成する""sについてはその対象ではありません。
提案の対象は次のものに関連するユーザー定義リテラルです
std::string_view
std::complex
<chrono>
これらのユーザー定義リテラルを使用する場合で何か問題がある場合、それは実行時ではなくコンパイル時にコンパイルエラーとして報告されます。それにより、ユーザーはバグに早期に気づくことができるようになり、実行時のデバッグにおいてもそれらのものを無視することができます。
この変更によって既存のコードが壊れる可能性があるのは、ユーザー定義リテラル演算子オーバーロード関数のアドレスを取っている場合です。consteval関数のアドレスはコンパイル時にしか存在できないため、そのようなコードはエラーになる可能性があります。しかし、標準ライブラリのほとんどの関数は基本的にそのアドレスを取ることが禁止されているため、実質的に既存コードを壊さないとしています。
std::hiveを定数式で使用可能にする提案。
C++26では既存の全てのコンテナとコンテナアダプタが基本的に定数式で使用可能になっています。その一方、同じくC++26で追加されたstd::hiveはほとんどの関数がconstexpr指定されていないことによって定数式で使用可能ではありません。
この提案は、C++26においてstd::hiveを定数式対応させようとするものです。
std::hiveはパフォーマンスを重視しているコンテナであり、特にキャッシュヒット率を向上させることを意識しています。そのため、コンテナの一部の操作などが定数化されてしまうことによる実行ファイルの肥大化と、(本来キャッシュ内で計算すればいいものを)定数のメモリからの読み出しに置き換えてしまうことによるパフォーマンスの低下を懸念してほとんどconstexpr対応は行われていませんでした。
この提案では、それはオプティマイザの仕事の結果でありconstexpr対応やその仕様とは関係ないこと(オプティマイザの定数畳み込みはconstexpr関数であるかどうかとは無関係に行われている)、constexpr変数の初期化などによって定数式をトリガーしないと定数化は行われないこと(ほとんど、自動的に起こることではない)などから、そのような懸念を否定しています。
MSVC STLのフォークによるconstexpr std::hiveの実装を行っており、そこで問題があったのは.get_iterator(const T *)のみだったとのことです。この関数はstd::hiveの要素のポインタを受け取って、そこからイテレータを取得するものです。その計算量は要素数に対して線形と指定されており、その実装はポインタのグローバルな順序付け(実装定義全順序)に依存しているようで、これが定数式では提供されないことが問題となります。
そのため、この提案では.get_iterator()をconstexpr指定することは提案していません。
この提案の著者の方は、.get_iterator()をconstexpr指定するために必要なユーティリティをP3852で提案しており、これが採択されてから改めて.get_iterator()をconstexpr指定することを推奨しています。
また、std::hiveの可能な実装戦略のうち少なくとも一つではあとからconstexpr対応させようとするとABI破損の可能性があるため、C++26でconstexpr対応しておくことを提案しています。
<cmath>ヘッダの参照をC23に更新する提案。
<cmath>ヘッダはCの<math.h>を参照しており、ほとんどの機能はC由来になっています。C++26時点では<cmath>ヘッダはC17の<math.h>を参照しています。
P3348R4ではC++26におけるCの参照をC23に更新していますが、10進浮動小数点数型に関するものやその他コア言語の変更が必要になるものがあったため<cmath>ヘッダの参照は更新されていません。
この提案は、コア言語に必要な対応を行ったうえで<cmath>ヘッダの参照をC23に更新するものです。これにより、C23までに追加されたいくつかの数学関数がC++でも利用可能になります。
ただしこの提案では、C23の10進浮動小数点数型(_Decimal128)をC++でも使用可能にすることは含んでいません。
C20以降で追加されたものは大きくISO/IEC 60559に準拠する数学関数と、マクロの2種類です。
そのうちNarrow rounding functionsと呼ばれる丸め関数群については、関数名の一部として型を指定する関数群をそのまま輸入するのではなく、1つの関数テンプレートとして使用可能にすることを提案しています。
コア言語について変更が必要なのは、浮動小数点数の正規化(canonicalization)に関してであり、このための文言も提案しています。
追加を提案しているものは多岐にわたるため、詳細は提案をご覧ください(関数については表にまとめられています)。
std::atomic_ref::address()の戻り値型をvoid*にする提案。
std::atomic_ref::address()はatomic_refが参照しているオブジェクトのポインタを取得するものです。これは取得したポインタを介してアクセスするためのものではなく、そのポインタ値そのもの(アドレス)を使用したい場合のためのものです(主に並行ハードウェアにおいての同期コスト削減のために使用する)。
ただ、この関数の戻り値型はatomic_ref<T>に対してT*であり、容易に間接参照して元のオブジェクトにアクセスする事ができてしまいます。しかし、それはアトミックアクセスではなくなるため、ほとんどの場合誤用となります。
そのためatomic_ref::address()の戻り値型はそのままだと間接参照することができないvoid*やuintptr_tにすべきだという意見もあり、その提案の途中のリビジョンではそうなっていた時期もありました。しかし、どちらの型も定数式で使用できない問題があったことから、最終的には関数名を変更(最初は.data()だった)したうえで戻り値型はatomic_ref<T>に対してT*を返すようにされました。
uintptr_tは必須の型ではないことによって定義されていない環境があり得ることが問題でしたが、これは解決のめどが立っていません。一方、C++26 P2738R1の採択によってvoid*とT*のキャストは定数式でサポートされるようになっています。
この提案は、atomic_ref::address()の戻り値型についての懸念を表明するNBコメントを受けて、その解決のためにatomic_ref::address()の戻り値型をvoid*に変更しようとするものです。
void*はそのままだと間接参照できないため元のオブジェクトにアクセスする事が簡単ではない一方、アドレス値を取得して利用する用途では全く問題がありません。これにより、元の目的を達成しながらNBコメントの懸念に対処することができます。
唯一の欠点は、atomic_ref<T>においてTのCV修飾をコピーする必要性から戻り値型が複雑になることだとしています。
namespace std{
template<typename T>
class atomic_ref {
...
using address_return_t = COPYCV(T, void*);
...
constexpr T* address() const noexcept;
constexpr address_return_t address() const noexcept;
...
};
}
C++29以降のTrivial Relocation機能の設計要件の提案。
P2786R13で提案されC++26 WDにマージされていたトリビアルリロケーション機能は、NBコメントでの反対意見などを受けてC++26から削除されることが決定されました。この提案はそれを受けて、ライブラリでの使用経験や要件から望ましいトリビアルリロケーション機能に対する設計要件を提示するものです。
この提案は、Proxyというライブラリ(標準ライブラリ入りを目指して提案されてもいる)において、型消去を扱う場合に移動(ムーブ/リロケーション)パフォーマンスを最大化する観点からトリビアルリロケーションに求める要件を検討しています。
型消去ラッパでは、型消去対象のオブジェクトを何らかのバイトバッファとして保持しているため、型消去ラッパ自体をリロケーションしようとする場合はそのバイトバッファに格納されているオブジェクトもリロケーションする必要があります。バイトバッファに格納されているオブジェクトの型がトリビアルリロケーション可能であることが分かっている場合、このリロケーションはバイトバッファのmemcpyで行うことができます。
しかし、P2786R13のトリビアルリロケーション可能な型の定義においては、何らかのfixup操作が必要な型が含まれてしまっています。よくあげられるところではそれは多態的な型(仮想関数テーブルを持つ型)であり、ARM64eなどの特定のプラットフォームにおいてPointer Authentication Codeへの対応が必要になります。
このような場合、型消去ラッパの保持するバイトバッファをリロケーションするにはmemcpyではなくそこに格納されている型に応じたリロケーションパスを呼び出する必要があります。このような型を想定しなければ一切ユーザーコード(型消去対象の型で定義されている操作)を実行しない単なるmemcpyで終わっていたのに対して、ユーザーコードを実行してバイトバッファの移動を行う必要があります。これは、単なるmemcpyあるいは多態的型を保持するポインタの単純なムーブと比較してパフォーマンスで劣ることになります。
提案では、Proxyライブラリにおいてこのようなリロケーション後のfixupを考慮する場合としない場合(あるいはする必要のある型消去ラッパなど)との間でリロケーションのパフォーマンスをいくつかのプラットフォームで計測したベンチマーク結果が掲載されており、fixupを考慮する場合に大幅なパフォーマンスの低下が起こることを報告しています。
そのため、この提案ではトリビアルリロケーション可能な型とはmemcpyによるビット単位のコピーでリロケーションが完了する型として、追加のfixupが必要な型を含まないようにすることを提案しています。この提案のこの定義では、多態的な型はトリビアルリロケーション可能ではなくなります。
これを柱として、この提案では次の設計要件を提示しています
- トリビアルリロケーション可能とは、ビット単位のコピーのことと定義
- トリビアルリロケーション可能な型とは、オブジェクト表現をバイト列として
std::memcpy/std::memmoveを用いてsizeof(T)分コピーし、適切にアラインされたストレージに再配置できる型のこと
- このコピーの後、宛先オブジェクトは元のオブジェクトとの寸断なく生存期間を開始し、元のオブジェクトの生存期間は終了する。この際にデストラクタの呼び出しやその他のfixup処理は発生しない
- この性質は完全にコンパイラによって決定可能であり、判定する型特性のユーザー定義特殊化は禁止される
- ムーブ可能ではない型でもトリビアルリロケーション可能にする
- 暗黙的に削除された、または意図的に定義されていないムーブ操作は、トリビアルリロケーションを無効化しない
- その表現が要件1を見たすクラス型ではその宣言されたムーブ操作の有無にかかわらず、トリビアルリロケーション可能となる
- これにより、ムーブ後の無効状態の導入を防止しつつトリビアルリロケーション可能になり、型の無効状態を考慮するユーザー定義リロケーション操作の必要性を排除できる
- 条件付きのトリビアルリロケーション可能性
- 型が、あるブール条件下でのみトリビアルリロケーション可能であることを宣言することを許可する
- このブール条件が満たされ、すべてのサブオブジェクトが要件1を満たす場合、その型はトリビアルリロケーション可能となる
- P2786R13の
trivially_relocatable_if_eligible(bool)を利用することを推奨
- これにより、ライブラリ作成者は独自の特性クエリ方法や複雑な特殊化ルールなどを導入することなく、型の性質に合わせてトリビアルリロケーションを活用できる
- 生存期間のセマンティクスの明確化
- トリビアルリロケーション可能なオブジェクトのwell-formedな生コピーを作成した後、新しいランタイムプリミティブを導入せずに、コピー先オブジェクトの生存期間が直ちに開始され、コピー元オブジェクトの生存期間が終了することを明確化する
- これにより、新しい関数が不要になり既存の最適化に関する知識が活用できる
- 低レベルコードでの使用時のガイダンスの提供
- 生存期間のルールが順守される場合でも、トリビアルリロケーションが一般的な低レベルパターンに対して安全であることを明確に文書化する
- SBOのバッファ拡張
- アリーナ・コンパクション
- mmapによる永続化
C++26からトリビアルリロケーション仕様が削除されたことでその仕様の再設計が必要となり、この提案はその再設計に際してライブラリ実装者の観点からほぼ必須の要件をまとめて提示するものです。
この提案はリジェクトされているようです(P4155R0がレスポンスとして提出されています)。
浮動小数点数型の表現可能な値のモデルについて、もう少し明確にする提案。
提案では、浮動小数点数型の細部についてのいくつか質問に答える形でC++標準の浮動小数点数モデルの行間を明確化していき、規定が必要な部分を浮き彫りにしています。
主に次のような点を明確にしようとしています
- 浮動小数点数リテラル(
0.0など)は常に正(非負)
doubleが符号付のゼロを表現できる場合、0.0は正のゼロであり、-0.0は負のゼロ
- 正と負のゼロを表現できる浮動小数点数型の場合、ゼロに対する単項
-は符号が逆のゼロを返す
- ISO/IEC 60559に準拠していることの意味の定義
- ある浮動小数点数型は、その値の表現がISO/IEC 60559に記載されいている浮動小数点数形式の一つであり
- かつ、ISO/IEC 60559に記載されている浮動小数点数値の集合を表現可能な場合、にISO/IEC 60559に準拠している
- ISO/IEC 60559に準拠していることは、その浮動小数点数型に対する演算がISO/IEC 60559の規定通りに振舞うことを意味しない
- 浮動小数点数型が表現できる値の指定
- 少なくとも有理数のサブセット
- 型の実装定義値表現によっては、次の非有限値を表現できる
- 無限大
- quiet NaN
- signaling NaN
- その他の実装定義の値
- これらの値についても、正と負の符号を持つ異なる値を表現できる
- 負の定義
- 値が負であるのはその値がゼロより小さい場合
- 符号ビットが立っていることはその値が負の値であることを意味しない
- 式の結果におけるゼロ
- 式の結果が正負のゼロを表現可能な浮動小数点数型である場合、式の結果としてどのゼロが選択されるかは実装定義
<=>、== !=、< <= > >=において、正のゼロと負のゼロは同値
- テンプレートにおける等価性の定義
- テンプレート引数の文脈(特にNTTP)において、浮動小数点数の等価性とは、ビット単位で同一(bitwise identical)であること
- ビット単位で同一(bitwise identical)であるとは
- 型
Tの値xとyは、std::bit_cast<U>(x) == std::bit_cast<U>(y)がtrueの場合にビット単位で同一とみなされる
Uは、Tの同じサイズでオブジェクト表現におけるパディングビットの数と位置がTと同じ仮想的な符号なし整数型
- 正負のゼロ、ペイロードが異なるNaNなどはビット単位で同一ではない
この提案は現在のC++標準の規定する浮動小数点数型とその演算についての振る舞いを変えるものではありません。明確になっていない行間の部分を明確に規定しようとするものです。
std::executionのコンセプトの判定に使用されるタグ型の命名を修正する提案。
std::executionのコンセプトはそれによって使用可能になるいくつかのCPOを持っています。例えば、senderに対するsenderファクトリ/アダプタが該当します。これらのCPOは入力の型に対してそのメンバ関数を考慮してカスタマイゼーションポイントを探索しますが、その際に同名の関係ないディスパッチを防止するために、コンセプトにアダプトするためには特定のメンバ型をコンセプトによって指定されるタグ型によって定義しておく必要があります。
このstd::executionにおけるタグ型はCPO名に対して_tプリフィックスを付加する命名になっています。そのコンセプトにアダプトする型を定義する場合は、メンバ型としてCPO名+_conceptの名前でこのタグ型のエイリアスを定義しておく必要があります。
namespace std::execution {
struct scheduler_t {};
template<class Sch>
concept scheduler =
derived_from<typename remove_cvref_t<Sch>::scheduler_concept, scheduler_t> &&
...;
struct receiver_t {};
template<class Rcvr>
concept receiver =
derived_from<typename remove_cvref_t<Rcvr>::receiver_concept, receiver_t> &&
...;
}
例えば、schedulerコンセプトの場合はscheduler型を定義する際に、その型をSとするとS::scheduler_conceptという名前の型としてstd::execution::scheduler_t型を取得できるようにしておく必要があります。
このようなタグ型はすでに標準ライブラリにもいくつか存在しているのですが、それらのタグ型はその役割によって大きく2つに分類されます。
- 曖昧性解消用のタグ型
- コンストラクタ呼び出しや関数呼び出しの曖昧性を解消するためのタグ型
- コンセプトタグ型
- ある特定のコンセプトのモデルとなる型を識別するためのタグ型
そして、既存のタグ型はこのカテゴリの違いによってどうやら命名規則が異なっています。
- 曖昧性解消用のタグ型:
name_t
- コンセプトタグ型:
name_tag
std::executionのタグ型は後者のコンセプトタグに該当するため、この命名規則に従っていません。この提案はC++26のうちにこれを修正しておこうとするものです。
曖昧性解消用のタグ型は、ライブラリ関数(コンストラクタ)の特定のオーバーロードを明示的に選択するために引数に指定して使用するタグの型です。
struct foo_t {};
inline constexpr foo_t foo = foo_t();
struct useful_class {
explicit useful_class(foo_t, int, int);
explicit useful_class(bar_t, int, int);
};
auto myVar = useful_class(foo, 1, 2);
標準ライブラリの中では次のものがこれに該当するタグ型として提供されています
std::defer_lock_t
std::from_range_t
std::in_place_t
std::piecewise_construct_t
std::unexpect_t
このタイプのタグ型は_tプリフィックスを持ちます。
コンセプトタグ型は、関数の呼び出し時に指定する形で使用されるのではなく、ユーザー定義型において特定の名前で定義されるものです。ジェネリックプログラミングの文脈でタグによって入力されたユーザー定義型の性質を特定し、その満たす性質(コンセプト)に応じたディスパッチを行うために使用されます。
struct foo_tag {};
template<class T>
concept foo = ~~~~;
template<class T>
void internal_algorithm(T, foo_tag);
template<class T>
void internal_algorithm(T, bar_tag);
template<class T>
void useful_template(T t) {
internal_algorithm(t, typename T::your_category());
}
struct MyFoolikeClass {
using your_category = foo_tag;
};
また、このようなタグ型の継承構造によって、対応する概念の階層構造を表現することができます。
標準ライブラリでこのタイプのタグに該当するのはイテレータのタグ型です。
struct forward_iterator_tag : input_iterator_tag {};
template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
...;
コンセプトタグの場合、タグ名はコンセプト名+_tagになり、メンバ型のエイリアス名は_category/_conceptのプリフィックスを持ちます。
std::executionで追加されているタグ型は、その命名規則を除いてコンセプトタグに該当するタイプのタグ型です。そのため、C++26の完成前にその命名を変更する必要があります。
sender_t:
- メンバ型名:
sender_concept
- コンセプト:
sender
receiver_t:
- メンバ型名:
receiver_concept
- コンセプト:
receiver
operation_state_t:
- メンバ型名:
operation_state_concept
- コンセプト:
operation_state
scheduler_t:
- メンバ型名:
scheduler_concept
- コンセプト:
scheduler
これらのタグ型は現在のところ互いに派生関係にはなく、それぞれ派生するタグ型を持ちません。しかし、std::executionの上に構築される予定のネットワークライブラリではstd::executionから派生したコンセプトが定義されることが予想されており、その場合にはこれらのタグ型から派生したタグ型が定義される可能性があります。その場合はさらにコンセプトタグへ該当するようになります。
execution::affine_onアルゴリズムの改善提案。
execution::affine_onはexecution::task(P3552R3)とともに導入されたsenderアルゴリズムで、senderとschedulerを受け取って、そのsenderが与えられたschedulerで完了するようなsenderを返すものです。この時、受け取ったsenderが受け取ったschedulerで完了するように既になっている場合、返されるsenderでは追加のスケジューリングを省略することができます。
コルーチン内で他の非同期処理(コルーチン or sender)を待機(co_await)する場合、非同期処理のネストはco_awaitによって通常のシングルスレッド実行コードと同じような見た目で書くことができるようになります。この時、ある非同期処理のco_await前後で、その一連の処理を実行する場所(実行コンテキスト、すなわちscheduler)は変化しないものと仮定されるのが自然です。
例えば次のような極端な例において
task<> fun() {
co_await ex::just();
auto v = co_await ex::just(0);
auto [i, b, c] = co_await ex::just(0, true, 'c');
try {
co_await ex::just_error(0);
} catch (int) {}
co_await ex::just_stopped();
int result = co_await []->ex::task<int> { co_return 42; }();
}
この例でco_awaitしているのはほぼすべてsenderですが(最後だけコルーチン)、このようなコードにおいてco_awaitの前後で実行場所が変化することは非直観的となることが分かると思います。例えば、fun()自体があるスレッドプール上で実行されているときに、その内部のいずれかのco_awaitの後でスレッドプールとは関係ない別のスレッドで実行されるようになっていることはどちらかと言えばバグと判断されると思われます。
このため、execution::taskではco_awaitする前後で実行場所(scheduler)が変化しないようにしています。すなわち、別の非同期処理をco_awaitする場合、その処理がどこのschedulerで実行され、また現在のschedulerを書きかえるような事を行ったとしても、非同期処理が完了してco_awaitによる待機が解除される際に元のschedulerに復帰して後続の処理(大本のコルーチンそのもの)を再開します。
この仕組みの事をScheduler Affinityと呼びます。Scheduler Affinityは他にも、コルーチン固有の落とし穴を回避するためにも役立ちます。
execution::affine_onはexecution::taskにおけるScheduler Affinity動作に使用されるsenderアルゴリズムで、execution::taskのコルーチン内のco_awaitにおいて、待機対象の処理が完了した後に元のschedulerに復帰するために使用されます。
schedulerの復帰には再スケジュールと呼ばれる処理が必要になり(実行コンテキストへの再投入、スレッドプールの場合はスレッドプールのキューへの投入など)、これはコストがかかります。そのため、この再スケジュールをする必要がないことが分かる場合(co_awaitする操作がschedulerを変更しないことが分かる場合)に再スケジュールを回避してそのまま元のschedulerで再開することが望ましく、execution::affine_onはこのような最適化が許可されているものです。
execution::affine_onはexecution::continues_onと同じシグネチャで同じ動作をするのですが、この点がexecution::continues_onと異なります。
しかしP3552R3のexecution::affine_onは非常に最小限の定義しかされておらず、仕様が不透明で設計も不完全でした。それに対してはP3796などで問題が指摘されており、この提案はその流れやNBコメントを受けてexecution::affine_onの設計を改善しようとするものです。
この提案では次の変更を提案しています
execution::affine_onの引数の修正
affine_on(sndr, sch) -> affine_on(sndr)
schedulerはaffine_onの上から(co_await式内で用意して)渡すのではなく、affine_onの返すsenderに接続(connect)されたreceiverの環境から取得する
- これは、
co_await sender-exprに対して、as_awaitable(affine_on(sender-expr), *this)(ここでの*thisはexecution::taskオブジェクト)で行われる接続操作を介して
execution::taskのプロミス型の環境からschedulerを取得する
- これは
co_awaitの前の実行コンテキストのschedulerと一致するため、scheduler引数無しで正しいschedulerを取得できる
- 失敗しない
schedulerの要求
affine_on(sndr)の目的は、渡された処理sndrを実行し元の実行コンテキスト(scheduler)上で完了すること
sndrの処理が完了した後のスケジューリング操作(as_awaitableで接続後のoperation_stateの処理)が成功しなかった場合(set_error/set_stoppedで完了した場合)、元のschedulerに復帰することができなくなるためaffine_onの目的を果たすことができなくなる
- このため、
affine_onで使用されるschedulerとreceiverは必ず成功するものであることを要求する必要がある
receiverに関連付けられた停止トークンはunstoppable_tokenであり
schedulerの完了シグネチャにset_error_t/set_stopped_tを持たないものであること
- すべての
schedulerがこれを保証できるわけではない
- 保証できない
schedulerでは、affine_onの使用(ひいてはtaskコルーチンにおけるco_awaitの使用)がコンパイルエラーとなる
- 標準の
schedulerではparallel_scheduler以外のschedulerでこの保証が実現可能
senderアルゴリズムにおけるexecution::affine_onカスタマイズ
schedulerを変更することがないsenderアルゴリズムにおいては、そのことをaffine_onに伝達するために返すsenderに対してaffine_onをカスタマイズできる必要がある
affine_onのsender型のメンバ関数としてtransform_sender(sndr)を追加する
- デフォルトでは恒等関数だが、子
senderを返すようにして実行コンテキストを変更しない事を通知する
- 例えば、
just, just_error, just_stopped, read_env, write_env, then, upon_error,upon_stoppedなどが(一部は条件付きで)該当する
change_coroutine_schedulerの削除
execution::affine_onのデフォルト実装の明確化
- ここまでの変更も踏まえて、以前の
execution::continues_onから踏襲されていた設計とは全く異なるaffine_onの定義が得られる
- 1~4の変更を組み込んだ動作をデフォルトの定義とする
execution::affine_onの名称変更
- ここまでの事を踏まえると、
affine_onという名前は適切ではない可能性がある
- 名称の代替案は特に挙げていない
この提案はまだ具体的な文言を伴っておらず、これらの変更点も確定したものではないようです。
constexpr std::hive提案(P3933R0)に対する、std::hive提案の著者からのコメント。
P3933R0(上の方)では、std::hiveのC++26でのconstexpr対応が提案されており、それが行われていない理由や検討の歴史、先行実装と問題点について報告しています。
この文書は、std::hive提案の著者の方によるP3933R0へのコメントで、技術的な側面、提案のヒストリーの問題点、現在の実装においての問題点、標準化手続きの問題点などについてコメントされています。
著者の方としてはstd::hiveのconstexpr対応には反対ではないものの、「完全な実装とテストケースが提供され、私(std::hiveの著者)以外の第三者によるテストが行われ、かつアーキテクチャ固有のパフォーマンス上の問題やその他の問題が確認されない場合に限り、constexpr std::hive の採用を検討する価値があると考える」としています。
ただし、C++26に急いで間に合わせる必要はなく、まず標準ライブラリの実装が出てくるのを待ち、そのうえで実行時パフォーマンスや実装負荷を考慮しながらconstexpr実装を行って、それから標準化を進めることを推奨しています。
転載はしませんが、技術的なコメントや実装におけるコメントにはstd::hiveの提案では語られていないような点も語られているので、興味のある人は読んでみるとよいかもしれません。
必ずチェックされ違反が起きたら終了するアサーションの設計検討を行う提案。
2025年11月のKona会議において、ルーマニアNBからのNBコメントとそれに付随する提案(P3911R0、上の方に解説があります)を受けて「無視できない契約アサーション(non-ignorable contract assertions)」と呼ばれる新しい言語機能をC++26に向けて追加することを決定したようです。この提案は、それを受けてその設計空間の探索やトレードオフの検討を行うものです。
この提案では、この無視できないアサーションをP2900の契約アサーションの枠組みの中で設計しようとしており、特に事前条件アサーションの動作モデルについて次のように定義しています
事前条件アサーションは、関数呼び出し時にfalseである場合にプログラムにバグが存在することを示す条件を表すものである。これは静的解析ツールの様なツールによって報告されることが推奨される事象である。
その後の動作は、強制アサーションを使用しているか設定可能アサーションを使用しているかなど、多くの条件に依存する
このような定義にしているのは、ベースとなる事前条件のモデルが「条件がtrueでない限り、プログラムの動作は未定義」というものであるためです。これは広く用いられているモデルであり、事前条件が破られた場合の動作を規定するとそれはもう事前条件ではなくなります(つまり、その動作は関数の動作の一部になります)。
上記の定義の無視できないアサーション(強制アサーション)のモデルは、一定の動作保証を与えつつも(モデルの中ではそれについて言及せず)、依然として事前条件違反はプログラムのバグであるとして扱うものです。
そして、次の3つの設計基準を定義しています
- 関数宣言は人間が容易に解析でき、簡潔である必要がある
- アサーションの主要な目的(どのような場合にバグがあるとみなすか、を伝達する)が、二次的な目的(バグのある呼び出し時に何が起こるか)よりも明確に示されている必要がある
- 強制アサーションを使用している人が、誤って設定可能アサーションを記述してしまうことが起こらないようにする
この上で、構文とセマンティクスの設計を検討しています。
まず構文については、P3911R0で提示されていたものを含む次の4つを候補としています
void fun(int number, int index)
pre (number > 0)
pre! (index >= 0)
pre! (index < size());
void fun(int number, int index)
pre? (number > 0)
pre! (index >= 0)
pre! (index < size());
void fun(int number, int index)
pre_maybe (number > 0)
pre_always (index >= 0)
pre_always (index < size());
void fun(int number, int index)
pre (number > 0)
pre<enforce> (index >= 0)
pre<enforce> (index < size());
AとBには次のような認識上の曖昧さがあります
void fun(vector& vec1, vector& vec2)
pre!(vec1.empty())
pre(!vec2.empty());
void container::fun()
pre not(empty());
また、AとBにはCにおける契約アサーション(検討段階)との互換性の観点からのリスクがあり、Cではpre(expr)のようにアサーションを関数呼び出し形式にしたい要望があります。これは、Cとの相互運用においてpre, post, contract_assertを何もしない関数マクロとして定義しておくことができるためです。とはいえ属性を考慮すると現在すでにこれは必ずしも満たされないため、この要件の重要性は不明だとしています。
Cでは、_maybeと_alwaysのサフィックスがアノテーションとしての重要な部分である述語に対してノイズになっています。
最後のDはP3400R2で提案中のラベル指定との互換性があるものです。しかし、設定可能アサーションの方が強制アサーションよりも短くなります。また、P3400はまだ設計中であり、構文についてはもこのまま導入されるかはわかりません。例えば次のようになる可能性があります
void fun(int * p)
pre<std::enforce> (p != nullptr);
void fun(int * p)
pre [[=enforce]] (p != nullptr);
このようになる(あるいはほかの形)場合、構文の一貫性が無くなり混乱を招くことになります。
Bには2つの利点があります
- 特定の動作をデフォルトにしないため、常に選択する必要があり追加の入力も軽微
assertマクロのために使用できなかったassertという名前に関する問題を解決できる
ただし、これによりpreのようなサフィックスなしのものが永遠に存在しなくなります。
NSAの言う安全性の観点から、安全なアサーションは最低限次の2つの点が保証されている必要があります
- アサーション内の述語が少なくとも一回評価されること
- アサーションによって保護された命令の実行に制御が進まないこと
この要件を満たしただけだとまだ実装依存の動作がいくつか許容されます。最も顕著なのはenforceとquick_enforceのどちらのセマンティクスを選択するかです。enforceセマンティクスでは必ずしもプログラムの終了が保証されないため、強制アサーションの目的を明確化する必要があります。
- アサーションによって保護されたコードを実行しないこと?
- どのコードも実行しないこと(無条件終了)?
強制アサーションにおいては、設定可能アサーションで議論の的になっているいくつかの側面について別の選択をすることもできます。
- アサーションの複数回の評価
const化
- 条件式からの例外送出の契約違反への変換
- 条件式で副作用を許容する
これらの決定には議論の余地があり、強制アサーションと設定可能アサーションの間で異なる設計上の選択をすることはそれはそれで大きな議論を呼ぶことになります。また、P3400のラベル機能が採択された場合、ほぼ同じ事を行うために複数の方法が存在することになります。
ただ、このような強制アサーションにおいて使用される式がある程度単純なもの(標準ライブラリの堅牢化事前条件のように)に留まる場合、別の選択をすることも可能かもしれません。例えば、条件式が例外を送出しうる場合をコンパイルエラーにするなどです。
強制セマンティクスにおいては条件式の評価回数を正確に一回にとどめることができるようになります。しかし、これには欠点もあります
- 呼び出し先でアサーションを評価する必要があるため、呼び出し元でエラーを報告する機能が失われる
- 事前条件違反が発生した場合、呼び出し元を指摘する必要がある
- プログラマが任意のコード(正当性とは全く関係のない、場合によっては副作用のあるコード)を関数呼び出しの前後に使用することを可能にし、促進する
この提案は設計空間の検討を行うのみで、特定の選択を提案してはいません。むしろ、このようなアサーションをここで入れることには反対の雰囲気があります。
文字列を返すリフレクションのメタ関数の戻り値型をstd::stringにする提案。
std::meta::identifier_ofなどの文字列を返すメタ関数の戻り値型はstd::string_view(もしくはstd::u8string_view)であり、これらの関数では返される文字列はnull終端が保証されることが明記されています。
struct C { };
constexpr string_view sv = std::meta::identifier_of(^^C);
static_assert(sv == "C");
static_assert(sv.data()[0] == 'C');
static_assert(sv.data()[1] == '\0');
ただしこのことは、既存のstd::string_viewの使用習慣と矛盾しています。通常std::string_viewはnull終端されていないものとして扱うことが推奨され、教えられており、標準ライブラリにおけるこれらの関数はその習慣とは真逆の事をしています。
また、コンパイラを始めとしたツールがstd::string_viewを介したアクセスの際にそのサイズを超えた場所を参照するようなコードを検出しようとする場合、標準規格が保証しているこれらのコードがFPとして検出されてしまうため、ユーザーかツールのどちらかが対応しなければならなくなります。
さらに、str[str.size()]のようなnull文字へのアクセスはstd::string_viewの事前条件違反であり、定数式では使用できません。ここにアクセスしたい場合はstr.data() + str.size()と書く必要があります。
リフレクションのAPIの他の部分を見てみると、std::vector<std::meta::info>を返す関数(members_of()など)ではstd::span<std::meta::info>ではなくstd::vectorを返しています。このことはstd::string_viewを返す関数と一貫していません。
この提案は、この問題の解決として、これらの関数でstd::string_viewを返す代わりにstd::stringを返すようにすることを提案しています。
対象となるのは次の関数です
identifier_of()
symbol_of()
display_string_of()
また、代案としては次の方法も挙げています
- null終端保証を削除する
- C++29の
std::cstring_viewを待つ
これらの関数がstd::string_viewを返すようになっているのは、これらの関数を次のように記述するためだったようです
constexpr auto name = identifier_of(r);
これは、コンパイル時の動的メモリ確保の制約(非一時的なアロケーションが許可されていない)により、std::stringを返す場合にコンパイルエラーとなります。しかし、このためのソリューションとしてdefine_static_string()が用意されているため、将来std::stringの非一時的なアロケーションが許可されるまではdefine_static_string()を使用することを推奨しています。
この提案はLEWGのレビューにおいて支持を得られず、リジェクトされています。
コンパイル時定数を関数の引数として渡す方法について、std::constant_wrapperを一貫して使用するようにする提案。
C++26で追加されたstd::constant_wrapper<V>はコンパイル時定数をNTTPとして保持して扱うための型で、特にconstexpr引数を実現するためのライブラリ機能です。
std::constant_wrapper<V>はほとんどすべての演算子をオーバーロードしていることによって、保持する値Vに対する透過的な操作を提供しつつ、それらの操作の結果が再びstd::constant_wrapper<V>を返すことで値が型の文脈に留まり続けるようにします。
一方で、C++26のstd::function_refではメンバ関数ポインタのサポートのためにstd::nontypeというNTTP値タグ型を導入しています。これはstd::nontype<+f>のようにして関数ポインタをNTTP値としてstd::function_refのコンストラクタに渡すためのもので、NTTP値として渡すことによって関数ポインタのストレージを省略し別の用途(メンバ関数呼び出しに必要な*thisオブジェクトの参照)に使用するためのものです。
std::nontypeはstd::constant_wrapperと同じ目的であるためそれを統一すべきという議論があったものの、std::constant_wrapperが関数呼び出し演算子もオーバーロードしていることによってstd::constant_wrapper<+f>のように渡した時にこの渡したものそのものが呼び出し可能になってしまうという問題があったことからstd::nontypeはstd::function_refのコンストラクタ専用のタグ型として残されることになりました(このとき同時にNTTPという用語が変化したことによる名称変更があり、最終的にstd::constant_argになった)。
結局現在のところ、constexpr引数を実現するための方法が2種類存在しています。基本的にはstd::constant_wrapperを使うべきですが、なぜ2種類あるのかやその使い分けなどは学習に当たってのノイズとなります。
この提案は、この現状に対してstd::constant_wrapperへの一本化を提案するものです。
std::function_refでstd::nontypeの代わりにstd::constant_wrapperを使う際の問題点はP3792R0で説明されていますが、それは簡単に言えばstd::cw<f>とfに同じ引数を与えて呼んだときに最終的な呼び出しの結果(戻り値)が異なる場合に、それを関数ラッパに入れるとstd::function_refとそれ以外の関数ラッパで呼び出し結果が異なってしまうことです。
この提案ではこれに対して、そのような曖昧さが生じる場合を検出してコンパイルエラーにすることを提案しています。
std::function_refのstd::constant_wrapperを受け取るコンストラクタ(現在はstd::constant_argを受け取っている)において、渡されたstd::constant_wrapperのf0に対してArgsが0より大きい場合、次のどちらかを満たすことを適格要件(Mandates)として要求します
f0(declval<ArgTypes>()...)がWell-formedではない
f0(declval<ArgTypes>()...)の型がstd::constant_wrapperの特殊化ではない
ここでのf0はstd::constant_wrapper<f>として渡されてきたものであり、まずArgs(std::function_ref<R(Args...)>)を直接渡して呼べないのであれば問題ありません(std::constant_wrapperの呼び出しは引数もstd::constant_wrapper互換である必要がある)。呼べてしまう場合でも、その戻り値型がstd::constant_wrapperでなければ問題ありません(std::constant_wrapperの呼び出し結果はstd::constant_wrapper)。
Mandates違反はSFINAEしないので、この曖昧さを検出したら即座にコンパイルエラーになります。
これにより、std::function_refに対してstd::cw<f>を渡した際にfの呼び出しをラップしたいのか、std::cw<f>の呼び出しをラップしたいのか曖昧になる、という問題が解消されます。そしてこれをもって、この提案ではstd::constant_wrapperを一貫して使用し、std::constant_argを削除することを提案しています。
おわり
この記事のMarkdownソース