[C++]WG21月次提案文書を眺める(2025年08月)

文書の一覧

全部で26本あります。

もくじ

N5013 Programming Languages - C++

C++26規格書のCommittee Draft。委員会に所属していないと見られないようです。

N5014 Working Draft, Standard for Programming Language C++

C++26のワーキングドラフト第8弾。

N5015 Editors' Report - Programming Languages - C++

↑の変更点をまとめた文書。

N5019 Business Plan and Convener's Report: ISO/IEC JTC1/SC22/WG21 (C++)

ビジネスユーザ向けのC++およびWG21の現状報告書。

P2414R10 Pointer lifetime-end zap proposed solutions

Pointer lifetime-end zapと呼ばれる問題の解決策の提案。

以前の記事を参照

このリビジョンでの変更は

などです。

P2843R3 Preprocessing is never undefined

プリプロセッサに存在する未定義動作を取り除く提案。

以前の記事を参照

このリビジョンでの変更は

  • EWGにおいて設計上の選択を確認
    • キーワードに一致するマクロはill-formedとなるべき
    • //コメント内で垂直方向のホワイトスペースをサポートする
  • フィードバックにより、一部のケースで現在のIFNDRを維持する
    • #includeディレクティブにおいて、マクロ展開後の標準形式のいずれとも一致しない場合
    • マクロが定義済みのpreprocessor operatorに展開される場合

などです。

この提案は2025年6月の全体会議で承認され、C++26に採択されています。

P3100R4 A framework for systematically addressing undefined behaviour in the C++ Standard

UB(及びEB)を契約違反として扱うようにする提案。

以前の記事を参照

このリビジョンでの変更は

  • P3754R0に合わせて提案を更新
  • Sofia会議でのフィードバックを適用
  • UBリストからUBとして指定されるべきではなく、CWG Issueがあるものを削除
  • noexceptとの相互作用に関する議論を追加
  • 提案の構成を改善
  • 提案のスコープを反映してタイトルを更新

などです。

P3337R0 Graph Library: Library Comparisons

提案中のグラフライブラリと既存のグラフライブラリの比較を行った文書。

この文書は一連のグラフライブラリ提案の一部として、提案中のグラフライブラリ(の参照実装graph-v2)と既存のグラフライブラリ(主にBoost Graph: BGL)との書き味とパフォーマンスについての比較を行った結果を報告するものです。

文書では、まずP3128 でTier1として提示されているアルゴリズムの一部について比較を行っています。扱われているアルゴリズムは次のものです

両ライブラリでこれらのアルゴリズムがどのように記述できるかを比較し、大きなデータセットにおけるその実行パフォーマンスを比較しています。

提案文書より、幅優先探索コードの比較。

BGL

using namespace std;
using namespace boost;

using G = compressed_sparse_row_graph<directedS, no_property, no_property>;
using Vertex = graph_traits<G>::vertex_descriptor;

G g;
// populate g

vector<Vertex> parents(num_vertices(g));

auto vis = make_bfs_visitor(
  make_pair(
    record_predecessors(parents.begin(), on_tree_edge())
  )
);

breadth_first_search(g, vertex(0, g), visitor(vis));

graph-v2

using namespace std;
using namespace graph;

using G = container::compressed_graph<void, void, void, uint32_t, uint32_t>;
using VId = vertex_id_t<G>;

G g;
// populate g

vector<VId> parents(size(vertices(g)));

auto bfs = edges_breadth_first_search_view<G, void, true>(g, 0);

for (auto&& [uid, vid, uv] : bfs) {
  parents[vid] = uid;
}

詳細な比較結果については提案を参照してください。

全体として、最新のC++機能を使用することでよりシンプルに記述できつつ、多くの場面でBGLを大きく上回るパフォーマンスを発揮しています。

P3347R4 Invalid/Prospective Pointer Operations

無効化されたポインタに対する一部の演算を明示的に許可する提案。

以前の記事を参照

このリビジョンでの変更は良くわかりませんが、編集者を追加したり、文書と文言の修正などのようです。

P3427R2 Hazard Pointer Synchronous Reclamation

ハザードポインタライブラリにSynchronous Reclamation機能拡張を追加する提案。

以前の記事を参照

このリビジョンでの変更は

  • hazard_pointer_asynchronous_reclamationhazard_pointer_try_reclamationにリネーム
  • retire_to_cohortの文言に、"May reclaim possibly-reclaimable members of c."を追加

などです。

P3428R2 Hazard Pointer Batches

ハザードポインタライブラリに複数のハザードポインタをまとめて構築・破棄する機能拡張を追加する提案。

以前の記事を参照

このリビジョンでの変更は

  • 事前条件を緩和し、それを文言に反映
  • reset_hazard_pointer_batchclear_hazard_pointer_batchにリネーム
  • span1, span2span_from, span_toに変更
  • なるべくNの代わりにspan.size()を使用する

などです。

P3643R1 std::to_signed and std::to_unsigned

整数型を対応する符号付/符号なしの整数型に簡易に変換する関数の提案。

以前の記事を参照

このリビジョンでの変更は

  • 制約を厳格化し、cv boolだけでなくCV修飾型全般を除外
  • 機能テストマクロにfreestandingを追加

などです。

P3688R2 ASCII character utilities

ASCIIに関連する文字の判定・処理関数群を提供する提案。

以前の記事を参照

このリビジョンでの変更は

  • ascii_case_insensitive_compare()の戻り値型の誤りを修正
  • ascii_case_insensitive_compare()の定義におけるstrong_orderingstd::プリフィックスを削除

などです。

P3692R2 How to Avoid OOTA Without Really Trying

P3064R2を要約した文書、および現在の実装ではOOTAが起こらないことを明確化する提案。

以前の記事を参照

このリビジョンでの変更は

  • volatileセマンティクスに関する変更について再検討
  • この提案が他のOOTA研究者にもたらす利点について言及
  • 著者リストの更新

などです。

P3702R1 Stricter requirements for document submissions (SD-7)

HTML形式の提案文書について一定のルールを設ける提案。

以前の記事を参照

このリビジョンでの変更は

などです。

P3719R1 std::is_vector_bool_reference

std::vector<bool>を検出する型特性を追加する提案。

以前の記事を参照

このリビジョンでの変更は

  • 概念・提案する文言・実装経験、について追加

などです。

P3739R2 Standard Library Hardening - using std::optional<T&>

std::inplace_vectorにおいて、std::optional<T&>を利用して堅牢化モードを導入する提案。

以前の記事を参照

このリビジョンでの変更は

  • exception_ptrの検査を追加
  • define_static_{string,object,array}を追加
  • constを追加

などです。

P3774R1 Rename std::nontype, and make it broadly useful

std::nontypestd::constant_argにリネームする提案。

以前の記事を参照

このリビジョンでの変更は

  • LEWGレビューを反映
    • std::nontypestd::constant_argへのリネーム
  • この提案はstd::nontypeのリネームのみを行うものであることを明確化
    • 以前の提案内容も残しておく
  • §2.2. Post-Sofia decisions にLEWGレビューの決定事項を要約
  • abstractの更新

などです。

この提案は、2025年11月の全体会議を通過し、C++26に採択されています。前述のように、リネームのみを行っています。

P3775R0 Slides for P3774R0 - Rename std::nontype

P3774R0の紹介スライド。

P3774R0については以前の記事を参照

このスライドではC++26タイムフレーム内でのstd::nontypeの経緯を説明し、P3774R0での変更を簡単に紹介しています。

P3779R0 reserve() and capacity() for flat containers

std::flat_map等のflatなコンテナに、reserve()capacity()メンバ関数を追加する提案。

std::flat_setstd::flat_mapstd::flat_multisetstd::flat_multimapはメモリ上で連続した領域にソート済み配列を構築することでメモリの局所性を高め、イテレーションのパフォーマンスを向上させた連想コンテナです。

これらのコンテナはいずれも内部で別のシーケンスコンテナを使用しており、デフォルトではstd::vectorが使用されています(最後の方のテンプレート引数で変えられる)。

namespace std {
  // flat_mapの宣言例
  template <class Key,
            class T,
            class Compare = less<Key>,
            class KeyContainer = vector<Key>,
            class MappedContainer = vector<T>>
  class flat_map;
}

flatコンテナはこれらの内部コンテナに関する操作をほとんど公開しておらず、特にreserve()の様なパフォーマンス上重要なインターフェースも公開していません。

そのため、flatコンテナでreserve()を行おうとすると、.keys()/.values()を使って得たconst参照をconst_castするか、.extract()を使って取り出したコンテナを.replace()で戻す、という方法を取らざるを得ません。

std::flat_map<std::string, std::string> fmap = ...;

// 1. 内部コンテナ参照をconst_cast
const_cast<std::vector<std::string>&>(fmap.keys()).reserve(100);     
const_cast<std::vector<std::string>&>(fmap.values()).reserve(100); 

// 2. 取り出した内部コンテナを戻す
auto tmp = std::move(fmap).extract(); 
tmp.keys.reserve(100); 
tmp.values.reserve(100); 
fmap.replace(std::move(tmp.keys), std::move(tmp.values)); 

1つ目の方法は、set系のコンテナが.keys()/.values()を提供していないためflat_setなどでは使えません。2つ目の方法は、これらのインターフェース特有の事情による注意事項がいくつかあるためリスクがあります。

この提案はこの問題の解決のために、.reserve().capacity()flatコンテナに追加することを提案しています。

std::flat_map<std::string, std::string> fmap = ...;

// proposed: 
fmap.reserve(100); 

// proposed: 
if (fmap.capacity() == fmap.size()) { 
  fmap.reserve(100); 
} 
  • .reserve()
    • 内部コンテナのどちらか片方がサポートしている場合に提供
    • サポートしている内部コンテナに対して対応する.reserve()を呼び出す
  • .capacity()
    • 内部コンテナの両方がサポートしている場合に提供
    • 内部コンテナの返す.capacity()の値の最小値を返す

そして、set系のコンテナに対して.keys()/.values()メンバ関数を追加することも提案しています。

std::flat_set<std::string> fset = ...;

// どちらの関数からも同じコンテナが取得できる
const auto& data1 = fset.keys();     
const auto& data2 = fset.values(); 

set系のコンテナの場合、key=valueであるので.keys()/.values()メンバ関数はどちらも同じ内部コンテナへのconst参照を返します。

P3790R1 Pointer lifetime-end zap proposed solutions: Bag-of-bits pointer class

ポインタの参照先の寿命が尽きた後でも使用可能なライブラリユーティリティを提供する提案。

以前の記事を参照

このリビジョンでの変更は

  • launder_bag_of_bits_ptr()bag_of_bits_ptr<T>launder_ptr_bits()ptr_bits<T>にリネーム
  • 以前のusable_ptr<T>に対する別名候補の追加

などです。

P3796R1 Coroutine Task Issues

std::taskの設計上の問題点を修正する提案。

以前の記事を参照

このリビジョンでの変更は

  • co_return { ... };をサポートする提案を追加
  • P3801R0への回答を追加
  • co_yield with_error(e)が使いにくい理由を追記
  • change_coroutine_schedulerschedulerが代入可能であることを要求するという議論を追加
  • operator co_awaitの追加に関する議論を追加
  • bulktask_schedulerに関する議論を追加
  • 右辺値修飾子の欠落に関する議論を追加
  • return_valuereturn_voidに関する仕様が欠落しているという問題を追加
  • その他小さな修正

などです。

この提案では、以前の記事の分類における"その他の設計上の修正"のカテゴリに次の問題を追加しています

  • co_return { ... };のサポート追加
    • プロミス型のreturn_value()はテンプレートパラメータにデフォルト値が無いことで{}を使用できない
    • テンプレート引数のデフォルトを設定することで{}による初期化をサポートする
  • co_await change_coroutine_scheduler(sched)では代入可能なschedulerが必要になっている
    • 効果の規定でstd::exchangeを使用していることでschedに代入可能であることを要求しているが、schedulerは通常それが必須ではない
    • -> std::exchangeを使用しない定義で置き換えるか、代入可能性をschedulerの置換可能性の判定に使用する
  • sender非対応コルーチンはtaskco_awaitできるべき
    • いくつかの制約(提案文書 3.5.15 Sender Unaware Coroutines Should Be Able To co_await A task を参照)を考慮すると、現時点ではtaskoperator co_awaitを追加しないことが妥当と思われる
  • task_schedulerでのbulkの使用に関する問題
    • 通常のsenderと異なり、コルーチンで使用されるschedulerの型はtaskの作成時に認識されている必要があり、接続時にカスタムschedulerを使用するためには、型消去スケジューラであるtask_schedulerを使用する
    • senderアルゴリズムを特定のスケジューラ向けにカスタマイズする場合、task_schedulerが(型消去されていることによって)そのカスタマイズを伝達できないことで、意図通りに動作しない場合がある
    • bulkアルゴリズムparallel_schedulerがその典型例
    • -> いくつかの選択肢を提示
  • 右辺値修飾子の欠落
    • task_schedulerにネストされたsenderは必ずしもコピー可能ではなく、操作は型消去されるため、task_scheduler::ts-sender::connectは右辺値修飾する必要がある
    • コルーチンはコピーできず、コルーチンハンドルを別のオブジェクトに転送する場合その操作は右辺値修飾する必要があるため、task<T, E>::connectも右辺値修飾する必要がある
  • return_valuereturn_voidの仕様の欠落
    • プロミス型のreturn_valuereturn_voidは宣言されてはいるもののその仕様が欠落している
    • -> 対応する文言を提案

また、P3801R0への回答では提示されている問題点はこの提案でもある程度カバーされており理解できるものの、std::execution::taskを削除する理由にはならないとしています。

こことP3801で挙げられている問題のうち緊急性のあるものについてはNBコメント対応やLWG Issue対応としてC++26に向けて修正するつもりのようです。

P3798R1 The unexpected in std::expected

std::expected.has_error()を追加する提案。

以前の記事を参照

このリビジョンでの変更は

  • 例ではstd::expected<void, std::string>を使用するようにした
  • フィードバックによる文言の改善
  • __cpp_lib_expectedの値を更新
  • 謝辞の追加

などです。

P3806R0 views::cycle

入力範囲を無限に循環する範囲を生成するRangeアダプタ、views::cycleの提案。

views::repeat(v)vの繰り返しによる範囲を生成するのに対して、views::cycle(r)は範囲rを循環イテレーションすることによる繰り返し範囲を生成するものです。現在これは、views::repeat(r) | views::joinを用いて近似できます。

std::vector<int> r = {1, 2, 3};

for (auto&& v : views::repeat(r) | views::join) {
  // 1, 2, 3, 1, 2, 3, 1, 2... という要素列をイテレーションし続ける
  ...
}

これにはviews::joinを通すことによってもっとも強くてもbidirectional_rangeにしかならないなどの問題があり、専用のアダプタを用意することで直観的に使用できるようになるとして、views::cycleを追加することを提案しています。

提案文書より、サンプルコード

for (auto&& song : playlist | views::cycle | views::take(100)) {
  play(song);
}

元の範囲を繰り返す性質上マルチパス保証が必要となるため、views::cycle(r)rforward_rangeである必要があります。

元の範囲をRとして、その他の性質は次のようになっています

  • rangeカテゴリ
    • Rrandom_access_rangeかつsized_range: random_access_range
    • Rbidirectional_rangeかつcommon_range: bidirectional_range
    • それ以外: forward_range
  • common_range: ×
  • borrowed_range: ×
  • sized_range: ×
  • const-iterable: const Rforward_rangeであれば

Range-v3にある同等のアダプタviews::cycledでは元の範囲の終端イテレータを積極的にキャッシュすることで、元の範囲のカテゴリを可能な限り維持しようとしますが、views::cycleではそれを行っていません(カテゴリがbidirectional_range/random_access_rangeになる場合の条件)。

また、views::cycleは同じ範囲を循環していることからiter_swapを提供しません。これは、イテレータとして異なる位置にある要素でも元の範囲上では同じ要素を指している可能性があり、その状況でiter_swapすると自己代入のようなことが起こってしまうためです。

P3809R0 Should we make std::linalg reductions deduce return types like fold algorithms?

std::linalgにあるリダクション系の関数の戻り値型の決定方法を変更しないようにすることを促す提案。

std::linalgにあるdot()vector_two_norm()などのリダクション(std::accumulateranges::fold_leftなどのように、範囲の各要素に何か処理をしてその結果を集計していくような計算)を行うような関数の戻り値型の決定方法は、std::reduceなどの設計を踏襲して初期値の型を戻り値型として使用します。しかし、C++23のranges::fold_leftなどfold操作においては、初期値と範囲の参照型を使用した二項演算の結果の型を推論して戻り値型として使用します。

// linalgのリダクション操作
std::mdspan<double, std::dims<1>> vec1 = ...;
std::mdspan<double, std::dims<1>> vec2 = ...;

std::same_as<int> auto dot = std::linalg::dot(vec1, vec2, 0); // ✅

// ranges::fold系リダクション操作
std::vector<double> vec3 = ...;

std::same_as<double> auto foldl = std::ranges::fold_left(vec3, 0, std::plus<>{}); // ✅

// std::reduce
std::same_as<int> auto reduce = std::reduce(vec3.begin(), vec3.end(), 0, std::plus<>{}); // ✅

このように、C++23以降のよく似た操作であってもその戻り値型の決定過程が異なります。また、ranges::fold_leftの戻り値型決定方法は、オリジナルのstd::accumulatestd::reduceも同様)において初期値の型で戻り値型が決まってしまうことが問題点として認識されていたことからそれを改善したものです。

また、数値アルゴリズムのRange版の提案(P3732R0)でもranges::fold_leftの設計を踏襲して同じ戻り値型の決定方法を採用していることもあり、std::linalgリダクション系の関数でもranges::fold_leftと同様の戻り値型の決定方法を取るべきではないかという声が上がったようです。

この提案は、それらの設計選択は合理的なものとしつつも、std::linalgは現在の動作が理に適ったものであるとして変更しない様にすることを提案するものです。

この理由としては次の事を挙げています

  1. 式テンプレートとの相性の良さ
    • std::reduceは式テンプレートによって計算が行われるような要素型をうまく扱えるが、ranges::fold_leftはそうではない
    • 数値計算分野においては式テンプレートが比較的良く使用されるため、考慮する必要がある
  2. BLAS規格との一貫性
    • BLASFortran 95インターフェースは、初期値の型によって戻り値型が決定される
  3. 戻り値型の簡単な制御
    • 線形代数領域と混合精度数値演算に精通しているユーザーは、戻り値型を推測するのではなく明示的に指定することを好む傾向にある

これらの理由から、std::linalgの現在の動作は線形代数計算の領域においては理に適ったものであるため、現状のまま変更しないことを推奨しています。

P3810R0 hardened memory safety guarantees

標準ライブラリ内の基本的な操作について、未定義動作の余地がないことを規定しておく提案。

C++26では標準ライブラリに堅牢化モードを導入し、一部の事前条件が堅牢化された事前条件としてC++26 Contractsの枠組みによって実行時にチェックされるようになります(P3471R4/P3607R0)。

コンテナ型はそこには当然含まれていますが、そのチェックにおいては.size().empty()といったメンバ関数が使用されます。これは実装を考慮すると未定義動作の余地なく実装可能と考えられますが、標準ではそのようなことを一切指定していません。

C++26 Contractsそのものが未定義動作が無いこと(契約チェック時のUBフリー)を保証してはいませんが、契約チェックに使用されるこれらの関数がUBフリーであることを保証できれば、少なくとも標準ライブラリの堅牢化モードの範囲においてはそのチェックがUBを伴わないことを保証することができます。

これらの理由からこの提案では、堅牢化モードのチェックに使用される標準ライブラリ中の基本的とみなされる操作について、未定義動作無しの実装を要求することを提案しています。

提案での対象は次の関数です

  • .size()
  • .empty()
  • イテレータの差分計算(-
  • .static_extent()
  • .extent()
  • .has_value()
  • .valueless_by_exception()
  • std::holds_alternative<I>()

これらの関数はメンバ変数の値を返すだけの単純な実装になるはずであり、UBの余地なく実装できるはずです。そしてそれはすでに行われているはずでもあるため、この提案による実装の変更は必要なく、実装者にUBフリーであることを要求することは不合理ではないとしています。

おわり

この記事のMarkdownソース