🍄

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

2021/01/24に公開

文書の一覧

採択されたものはありません、全部で32本あります。

N4878 Working Draft, Standard for Programming Language C++

C++23ワーキングドラフト第3弾。

N4879 Editors' Report - Programming Languages - C++

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

前回会議で採択された提案文書とコア言語/ライブラリのIssue解決が適用されているようです。

P0401R5 Providing size feedback in the Allocator interface

アロケータが実際に確保したメモリのサイズをフィードバックすることのできるメモリ確保インターフェースを追加する提案。

以前の記事を参照

このリビジョンでの変更は、LWGのフィードバックを受けて提案する文言を改善したことです。

P0561R5 An RAII Interface for Deferred Reclamation

deferred reclamationを実現するためのより高レベルAPIを標準ライブラリに追加する提案。

deferred reclamationは並行処理における複数スレッド間のデータ共有におけるパターンで、この提案では次のように説明されています。

ある1つのデータ(変数)に対してreaderupdaterの2種類のコンポーネントを考えます。readerupdaterも複数存在し、各々別々のスレッドからデータにアクセスします。
readerは読み取りロックを取得してデータを読み取ります。ここでは、そのロックが保持されている間そのデータの生存が保証されます。一方、updaterはデータを新しく確保された値によって置き換えることでデータを更新します。更新以降にデータを読みだしたreaderは新しい値を読み取りますが、置換前の古いデータを読み取った全てのreaderがロックを解除するまでは古いデータは破棄されずに生存します。
readerの読み取り操作は他のreaderupdaterをブロックせず、updaterreaderをブロックしません。データの更新はメモリ確保を必要とするため高コストですが、読み取りと比較すると非常に稀であることが想定されます。

このdeferred reclamationの実装には参照カウントやread-copy update(RCU)、ハザードポインタなどの方法があり、そのうちのいくつかは過去にC++に対して提案されています。しかし、それらはより実装そのものに近い低レベルなAPIを提供するものであり、それらの利用例の一つとしてdeferred reclamationが実現できるものでしかありませんでした。

この提案は、そのような低レベルなプリミティブによるものよりも安全性と使いやすさを重視し、かつ効率的な実装を可能とするdeferred reclamationだけのための高レベルなAPIを提供するものです。

#include <snapshot> // 新ヘッダ

// Configクラスによる設定を用いてリクエストを処理するServerクラス
class Server {
public:

  // 設定は随時変更可能
  // 設定変更を調べる別のスレッドから更新される
  void SetConfig(Config new_config) {
    config_.update(std::make_unique<const Config>(std::move(new_config)));
  }

  // リクエストはその時点の設定を使用して処理する
  // 設定の更新タイミングを考慮する必要はない
  void HandleRequest() {
    // リクエスト処理開始時点での設定データの取得
    std::snapshot_ptr<const Config> config = config_.get_snapshot();
    // configはunique_ptr<const Config>のように使用可能
    // configの生存期間内に設定データが更新されたとしても、configが参照するデータに影響はない
  }

private:
  // 共有される設定データ
  // 読み取り、更新、いずれに際しても同期を必要としない(ロックフリー)
  std::snapshot_source<Config> config_;
};

この提案のAPIはGoogle社内で実装され使用されているものをベースにしており、そこでは高レベルAPIとRCUによる実装の低レベルなAPIの両方が提供されているようですが、高レベルAPIの利用者が低レベルAPIに比べて多く、その経験こそがdeferred reclamationのための高レベルAPIを提供する価値を実証していると主張しています。

P0849R6 auto(x): decay-copy in the language

明示的にdecay-copyを行うための構文を追加する提案。

以前の記事を参照

このリビジョンでの変更は、EWGでのレビュー中に明らかになったいくつかの問題と、ライブラリの文言変更の意味合いを明確にする表を追記したことです。

この提案は現在LEWGにおけるライブラリパートのレビューを待っており、それが終了次第CWGに送られる予定です。

P0901R8 Size feedback in operator new

::operator newが実際に確保したメモリのサイズを知ることができるオーバーロードを追加する提案。

以前の記事を参照

このリビジョンでの変更は、LEWGでのレビューを受けて引数名を変更したことと、提案する文言を洗練させたことです。

この提案はこのリビジョンをもってLEWGでの投票にかけ、コンセンサスが得られればCWGに送付される予定です。

P1030R4 std::filesystem::path_view

パス文字列を所有せず参照するstd::filesystem::path_viewの提案。

std::filesystem::pathクラスはパスを表現するクラスですが、その実態はパス文字列であり、std::stringと同様にパス文字列を所有しています。したがって、構築や連結などの操作において動的メモリ確保が発生します。
std::filesystem::pathに対する操作のいくつかは新しいpathオブジェクトを返します。そこではメモリ確保と文字列のコピーが発生します。

例えば、ディレクトリを列挙する様な場合には1つのディレクトリの列挙毎にpathオブジェクトの構築コストがかかる事になり、ディレクトリの数が多い場合にはボトルネックとなります。また、pathオブジェクトの構築に伴う新規メモリ確保と文字列のコピーはCPUのキャッシュにも優しくありません。Windowsのパス文字列制限260文字に遭遇したことのある人が多くいる様に、パス文字列は数百バイトに達することもあり、パス文字列のコピーの度にキャッシュから有用なデータを削除する事になります。

std::filesystem::path_viewstd::filesystem::pathを参照する軽量なViewです。std::filesystem::pathと同様のインターフェースを提供し、ローカルプラットフォームのパス文字列に対してconst/constexprな参照であり、std::filesystem::pathとほぼ同様に振舞います。これによって、現在std::filesystem::pathを受け入れている所をリファクタリングをほぼ必要とせずにstd::filesystem::path_viewを受け入れられる様にすることができます。

また、std::filesystem::path_viewに対するイテレーションでpath_viewを返さない様にするために、std::filesystem::path_view_componentも追加されます。これはpath_viewとほぼ同じものですが、パス要素のイテレーションや抽出のための一部のメンバ関数を提供していません。
path_viewに対するイテレーションで得られた各パス要素をさらにパスとして扱う事は意味がなく、またそれを行う事はバグの可能性が高いため、パス要素である事を表現するための別の型が必要とされたのだと思われます。

この提案はC++23入りを念頭に作業が進められているようです。

P1072R6 basic_string::resize_and_overwrite

std:stringに領域(文字長)を拡張しつつその部分を利用可能にする為のメンバ関数resize_and_overwrite()を追加する提案。

例えばパフォーマンスに敏感なところで、std::stringに文字を流し込んでいく処理を書くとき、おおよそ次の3つのいずれかを選択することになります。

  1. 追加の初期化のコストを捧げる : resize() ゼロ初期化してから元の文字列をコピー
  2. 追加のコピーコストを捧げる : 一時バッファに文字列をためておき、最後にまとめてコピー
  3. 追加の簿記コストを捧げる : reserve() その後文字列が追加されるたびに、残りの長さが調べられ、null終端される

ここでやりたいことは、断続的に取得される文字列を随時追記していき最後にまとめて1つの文字列として扱う事です。しかし、いずれの方法も何かしら余分なコストがかかってしまい、最適な方法はありませんでした。

問題なのは、この様な場合にstd::stringをバッファとして使おうとしても、その領域をある程度の長さで確保しつつそのままアクセス可能にする、という操作が欠けていることです。

resize_and_overwrite()はまさにそのためのもので、指定された長さに領域を拡張しつつ、増やした領域はデフォルト初期化するだけに留める関数です。

namespace std {

  template <class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
  class string {

    template<typename Operation>
    void resize_and_overwrite(size_type n, Operation op);

  };
}

resize_and_overwrite()は1つ目の引数に変更したい長さをとり、現在の長さがそれよりも短い場合は追加された領域はデフォルト初期化されています。また、2つ目の引数に変更後の領域に対する初期化処理を書く事ができ、変更後の領域の先頭ポインタと1つ目の引数nを取る任意の関数を指定できます。operase(begin() + op(data(), n), end())の様に呼び出されるため、opは処理後に残しておきたい領域のサイズを返す必要があります。

P1102R2 Down with ()!

引き数なしのラムダ式の宣言時に()をより省略できるようにする提案。

以前の記事を参照

このリビジョンでの変更はCWGから得られたフィードバックを反映した事です。

この提案はコア言語に対するIssue報告から始まっており解決のための議論はすでに済んでいるようです。この提案は解決のための文言を確認・議論するためのもので、初めからCWGで議論されており現在はレビュー待ちです。そのため、特に問題が無ければ早めに採択されそうです。

P1315R6 secure_clear (update to N2599)

特定のメモリ領域の値を確実に消去するための関数secure_clear()の提案。

以前の記事を参照

このリビジョンでの大きな変更は、関数名がsecure_clearからmemset_explicitに変更されたことです。他の変更はC言語に向けた文言の表現の選択肢の追加や文言表現の修正、提案文書全体のマイナーな調整などです。

P1478R6 Byte-wise atomic memcpy

アトミックにメモリのコピーを行うためのstd::atomic_load_per_byte_memcpy()/std::atomic_store_per_byte_memcpy()の提案。

以前の記事を参照

このリビジョンでの変更はLEWGなどからのフィードバックを受けて提案する文言を修正した事とそれに伴って文言に関する未解決の質問のセクションを追記した事が主です。

P1642R5 Freestanding Library: Easy [utilities], [ranges], and [iterators]

[utility]<ranges><iterator>から一部のものをフリースタンディングライブラリに追加する提案。

前回の記事を参照

このリビジョンでの変更はstd::quick_exitstd::_Exitに依存しているため、_Exitも対象に追加した事。および一部のエンティティ名の(提案文書としての)参照方法を変更した事です。

P1689R3 Format for describing dependencies of source files

C++ソースコードを読み解きその依存関係をスキャンするツールが出力する依存関係情報のフォーマットを定める提案。

モジュール導入以前は、各翻訳単位のコンパイルは独立して行うことができ、翻訳単位の依存関係はビルドしながら把握すれば十分でした。しかしモジュールを使用すると、ある翻訳単位をコンパイルするためにはそこでインポートされているモジュールのコンパイル(少なくともインターフェースの抽出)が必要となります。
すなわち、モジュールを利用したプログラムでは各翻訳単位のコンパイルに順序付けが必要となります。

このために、ビルドツールはコンパイルする前にこの順序関係を(C++コードとしてではなく)ソースコードから抽出できる必要があります。

このフォーマットは次のような情報を含みます。

  • 依存関係スキャンツールそのものの依存関係
  • スキャンされた翻訳単位がコンパイルされる時に必要となるリソース
  • スキャンされた翻訳単位がコンパイルされた時に提供されるリソース

このフォーマットはその表現としてJSONを使用しその規格を参照しています。そのエンコーディングはユニコードであり、特にファイルパスはUTF-8の有効な文字列であることがさらに要求されます。

例えば次のようなソースコードに対しては

export module my.module;

import other.module;
import <header>;

#include "config.h"

次のようになります。

{
  "version": 1,
  "revision": 0,
  "rules": [
    "work-directory": "/scanner/working/dir",
    "inputs": [
      "my.module.cpp"
    ],
    "outputs": [
      "depinfo.json"
    ],
    "depends": [
      "/system/include/path/header",
      "include/path/config.h"
    ],
    "future-compile": {
      "outputs": [
        "my.module.cpp.o",
        "my_module.bmi"
      ],
      "provides": [
        {
          "logical-name": "my.module",
          "source-path": "my.module.cpp",
          "compiled-module-path": "my_module.bmi"
        }
      ],
      "requires": [
        {
          "logical-name": "other.module"
        }
        {
          "logical-name": "<header>",
          "source-path": "/system/include/path/header",
        }
      ]
    }
  ]
}

なお、この動機となったモジュールのコンパイル順の問題はFortranのモジュールが長年抱えている問題と同じものであり、このフォーマットはC++だけではなくFortranでも使用することを想定しているようです。

P2077R2 Heterogeneous erasure overloads for associative containers

連想コンテナに対して透過的な要素の削除と取り出し方法を追加する提案。

以前の記事を参照

このリビジョンでの大きな変更は、ヘテロジニアスerase/extractのキーを受け取る引数型をconst K&からK&&へ変更したことです。

当初のヘテロジニアスerase()の宣言は次のように提案されていました。

template <class K>
size_type erase( const K& x );

加えて、このキーに変換可能な型Kiterator/const_iteratorへ変換可能ではない事が要求されています。そのため、Kの全ての値カテゴリからiterator/const_iteratorへの変換可能性を調査している最中に次のような問題が発見されました。

// Compare::is_transparentが有効で型を示すstd::map
using map_type = std::map<...>;

struct HeterogeneousKey {
  HeterogeneousKey() { /*...*/ }

  // 右辺値参照修飾された変換演算子
  operator map_type::iterator() && { /*変換処理*/ }

  // map_type::key_typeとの比較演算子
  // ...
};

void foo() {
  map_type m;
  HeterogeneousKey key;

  m.erase(key); // コンパイルエラー
}
  1. keyの型からテンプレートパラメータKHeterogeneousKeyと推論される
  2. std::is_convertible_v<HeterogeneousKey&&, iterator>trueとなる(テンプレートパラメータ制約を満たしていないと判定される)
  3. ヘテロジニアスerase()はオーバーロード候補から外れ、最適な関数は見つからない・・・

ここでeraseに渡っているkeyは左辺値であるため、map_type::iteratorへは変換できないはずで、コンパイルエラーにはならないはずです。しかし、const K&という引数型になっていることで、テンプレートパラメータKには実際の引数の値カテゴリの情報が正しく伝播していません。そのため、何が渡されてもKprvalueにしかなりません。

この問題を解決するには正しく引数の値カテゴリの情報をテンプレートパラメータに伝播させればよく、それはforwarding referenceによって実現できます。つまりK&&となります。

template <class K>
size_type erase(K&& x);

この変更によって先程のコードはコンパイルが通るようになり、期待通りヘテロジニアスerase()を呼び出します。また、右辺値を渡したときはテンプレートパラメータ制約を満たせなくなり、コンパイルエラーとなります。

void foo() {
  map_type m;

  m.erase(HeterogeneousKey{...}); // コンパイルエラー
}

この問題及び解決策はstd::mapに限定されるものではなく、erase()だけではなくextract()でも同様です。

これ以外の変更は、標準の文書に合わせて文字のフォントや修飾を調整した事です。

この提案はこのリビジョンをもってLEWGでの投票にかけられ、問題が無ければC++23導入を目標としてLWGに送られる予定です。

P2136R2 invoke_r

戻り値型を指定するstd::invokeであるinvoke_rの提案。

以前の記事を参照

このリビジョンでの変更は提案する文言を修正した事です。

この提案は(R1が)LEWGでの投票にかけられ、問題が無ければC++23導入を目標としてLWGに送られる予定です。

P2175R0 Composable cancellation for sender-based async operations

Executor提案(P0443R14)に対して、非同期処理のキャンセル操作を追加する提案。

現在のExecutor提案には、実行コンテキストに投入した非同期処理がキャンセルされたときに処理が空の結果で完了しそれを通知する機能を提供していますが、非同期処理を呼び出し元がキャンセルするための機能は提供されていません。

非同期処理のキャンセルという操作は並行プログラムにおける基礎的なプリミティブであり、それによって並行処理を構造的に記述できるようにするために、非同期処理のキャンセル操作と、それを伝達するためのメカニズムをExecutorに導入しようとする提案です。

このためには、個々の非同期処理でアドホックなメカニズムによって呼び出し元がキャンセル要求を伝達できるようにする必要があります。例えば、std::chrono::durationを渡すことでタイムアウトによるキャンセルを行う、std::stop_tokenを非同期関数/コルーチンに渡す、又は非同期処理を表現する呼び出し可能な型のメンバとして.cancel()を実装する、などです。

キャンセルを適切に処理するためには、全てのレイヤーがキャンセルをサポートし、そのキャンセル要求は全てのレイヤーに適切に伝播しなければなりません。例えば、タイマーやI/O、ループなど、高レベルの処理の完了が依存しているより低レベルな処理に要求を伝達しなければなりません。

しかし、アドホックメカニズムを使用すると、キャンセル可能な処理を構成してそのキャンセル要求を中間層を介して伝播することが困難になります。これは特にwhen_all()アルゴリズムなど、構築済みのsenderによって構成されるアルゴリズムに当てはまり、入力となる元のsenderを作成した非同期処理に渡されるパラメータを制御する方法がありません。

この様な事を考慮したうえで、この提案の目指すキャンセル操作は次のような設計に基づきます。

  • 汎用的に構成可能なキャンセルのためのメカニズムを用意する。これによって、キャンセルに対して透過的であるか、新しいキャンセルスコープを導入して、キャンセル操作を処理のチェーンに挿入できるアルゴリズムを構築できる。
    • たとえば、指定された時間が経過した場合にキャンセル要求を行う、ジェネリックなtimeout()アルゴリズムを定義できるようにする。
  • キャンセル要求が行われないことがコンパイル時に分かる場合、キャンセルのためのオーバーヘッドがかからないようにする。
  • 非同期処理の呼び出し元と呼び出された側のいずれに対しても、キャンセルのサポートを強制しない。
    • キャンセルのサポートはオプトイン(デフォルトは非サポート)
    • 呼び出し元がキャンセルを要求しない場合は、キャンセルをオプトアウトするために何もする必要が無い

そして、このために次のような変更を提案しています。

  • std::stop_tokenと同様に扱えるクラスを表すための、std::stoppable_tokenコンセプトを追加する
    • 特定のユースケースにおいてより効率的なstop_token-likeな同期プリミティブをstd::stop_tokenの代わりに使用できるようにする
  • std::stoppable_tokenを改善版である2つのコンセプトを追加する。
    • std::stoppable_token_for<CB, Initializer> : stoppable_tokenであることに加えて、stop_tokenのインスタンスとInitializerの値からT::callback_type<CB>が構築可能であることを表す。
    • std::unstoppable_token : stop_possible()メンバ関数がconstexprであり常にfalseを返すstop_tokenを表す。
  • その操作に使用するstop_tokenconnectされたrecieverが取得できるようにするためにget_stop_token()CPOを追加する。
  • recieverに関連付けられたstop_tokenの型を求めるための型特性を追加する。
    • std::stop_token_type_t<T>は、型Tを引数として呼び出されたget_stop_token()の戻り値型をdecayして取得する
  • std::stoppable_tokenコンセプトを満たす2つの新しいstop_tokenを追加する
    • std::never_stop_token : キャンセルが不要な場合に使用されるstop_token
    • std::in_place_stop_token : stop_sourcemovable/copyableである必要が無く、stop_tokenの生存期間が対応するstop_sourceの生存期間内に厳密に収まっている場合に使用できるstop_token
  • std::stop_tokenにメンバ型::callback_typeを追加する(std::stoppable_tokenコンセプトのために必要)

提案されているものは、facebookによるC++ Executorの実装であるlibunifexにて既に実装されているようです。

P2186R1 Removing Garbage Collection Support

ガベージコレクタサポートのために追加された言語とライブラリ機能を削除する提案。

以前の記事を参照

このリビジョンでは、EWGおよびLEWGでの投票の結果が追記されています。概ね、これらのものを削除することに異論はないようです。

P2195R1 Electronic Straw Polls

委員会での投票が必要となる際に、メールまたは電子投票システムを用いて投票できるようにする。

以前の記事を参照

このリビジョンでの変更はよく分かりません。LEWGではこれに従った運用が始まっているようです。

P2213R1 Executors Naming

Executor提案(P0443R13)で提案されているエンティティの名前に関する報告書。

以前の記事を参照

このリビジョンでの変更はLEWGのレビューを受けて現在の名前がなぜその名前なのかの根拠を追記した事とP1897関連の名前についてをP2252(未発行)に移動した事です。また、次のものは指示を得られなかったため削除されたようです。

  • connect -> pipe
  • submit -> start
  • sender, receiver -> producer, consumer

また、LEWGはoperation_state(コンセプト)とset_done(CPO)の2つについては変更の必要性を強く認めているようです。

P2216R1 std::format improvements

std::formatの機能改善の提案。

以前の記事を参照

このリビジョンでの変更は以下のものです

  • LEWGのフィードバックを受けて、パラメータパックに非フォーマット文字列を渡したときに診断不用のill-formedとなる動作を削除
  • コンパイル時のチェックについて、診断(コンパイルエラー)を保証し、C++20機能と説明専用クラスだけを用いて実装できるように文言を調整
  • コンパイル時のフォーマットチェックについて実装例を追記
  • コードサイズの肥大化の問題について、実装品質の問題と同様に解決不可能であることを明確化
  • コードサイズの肥大化が実際に起こる例を追記

また、R0のLEWGでの投票の結果も追記されています。この提案にある2つの問題についての議論に時間を割くこと、フォーマット文字列のコンパイル時チェックに失敗したらコンパイルエラーとなる事が望ましいなどのコンセンサスが得られています。

P2233R3 2020 Fall Library Evolution Polls

LEWGが2020年秋に行った投票の対象となった提案文書の一覧。

前回との差分はよく分かりません・・・

P2238R0 Core Language Working Group "tentatively ready" issues for the November, 2020 meeting

11月に行われた全体会議で採択された、6つのコア言語のIssue報告とその解決の一覧。

概要だけを記載しておくと

  1. 構造体(集成体)に対する構造化束縛がmutableメンバを考慮するようになった
  2. 制約によるオーバーロード候補からの除外を、テンプレートパラメータの置換よりも先に行うようにする
  3. “flowing off the end of a coroutine”という用語の意味を明確にする
  4. 展開されていないパラメータパックが、その外側の関数型に依存しないようにする
  5. C言語リンケージを持ち、制約されているfriend関数の複数の宣言が、同じ関数を参照するようにする
  6. requires節にboolにならない(atomic constraintではない)有効な式を指定することがill-formedである事を規定

P2247R1 2020 Library Evolution Report

LEWG(Library Evolution Working Group)の今年2月以降の活動についてまとめた文書。

前回との差分はよく分かりません・・・

P2248R1 Enabling list-initialization for algorithms

値を指定するタイプの標準アルゴリズムにおいて、その際の型指定を省略できるようにする提案。

以前の記事を参照

このリビジョンでの変更は以下のものです

  • R0では議論されていなかったABI互換性についての問題を追記
  • P1997R1への参照と議論を追加
  • [algorithms.requirements]への参照を追加
  • スペルミスやフォーマットの修正

この提案の変更は関数テンプレートのテンプレートパラメータについてのもので、ABIを破壊するものではありません。しかし、std::ranges名前空間の下にあるrangeベースのアルゴリズムでは、テンプレートパラメータの並べ替えが必要になり、それによってマングル名が変化するためABI破壊の可能性があります。

とはいえ、並べ替えられる前と後で対応するテンプレートパラメータに同じ型を指定してインスタンス化する時にのみマングル名の衝突が発生するため、それが起こる可能性は非常に低いはずです(コンセプトのチェックによってコンパイルエラーとなる場合がほとんどのはず)。この様な衝突が発生しないとすれば、ABI非互換による問題は回避されます。

ただ、それら関数テンプレートの明示的インスタンス化が使用されている場合、ABI破壊が発生する可能性があります。ただ、そのような行為はどうやら許可されていないようでもあります。

これらの事を問題となるか、またするかどうかは今後議論されるようです。

P2262R0 2020 Fall Library Evolution Poll Outcomes

2020年11月に行われた全体会議におけるLEWGでの投票の結果。

投票にかけられる提案の一覧はP2233R3 2020 Fall Library Evolution Pollsにあります。

ここでは、投票の結果及び投票者のコメントが記載されています。

P2263R0 A call for a WG21 managed chat service

WG21が管理するチャットサービスを確立するための提案。

現在WG21は、freenodeのIRCチャネルとSlackのcpplangワークスペースの2つのチャットサービスを利用しているようですが、これらはWG21のメンバによって維持・管理されているものではなく、WG21行動規範やISO行動規範に従って管理されているものでもありません。

WG21の慣行及び手順に基づいて管理されるWG21が後援・維持するチャットサービスが必要とされたため、この提案はその検討と議論のためのものです。主に、チャットサービスに求める要件が列挙されています。

P2264R0 Make assert() macro user friendly for C and C++

assertマクロをC++の構文に馴染むように置き換える提案。

assertマクロは一引数の関数マクロとして定義されており、プリプロセッサは丸括弧のペアのみを考慮します。したがって、次のようなコードはコンパイルエラーとなります。

#include <cassert>
#include <type_traits>

using Int=int;

void f() {
  assert(std::is_same<int,Int>::value); // コンパイルエラー
}

関数マクロの呼び出しにおいては、丸括弧で囲まれていないカンマの出現は引数の区切りとみなされます。その結果、このコードはassertマクロを2引数で呼び出そうとすることになり、コンパイルエラーとなっています。
この事は、巧妙なコードを書く必要があるとはいえCでも同様です。

#include <assert.h>

void f() {
  assert((int[2]){1,2}[0]);   // コンパイルエラー
  struct A {int x,y;};
  assert((struct A){1,2}.x);  // コンパイルエラー
}

Cでは、NDEBUGが定義されていない時にassertの引数に指定された式がwell-definedならばそのままコンパイルされる必要があり、C++もその点に関してC標準を参照しています。

これらのように、現在のassertマクロの定義は適切ではなく初学者にも優しく無いため、このような事が起きないように定義し直そうという提案です。

例えば、可変長引数を利用して次のように定義する事を提案しています。

#define assert(...) ((__VA_ARGS__)?(void)0:std::abort())

ただ、これだと0引数で呼び出すことができてしまい、その場合に分かりづらいコンパイルエラーが発生する可能性があります。しかし、1つの引数+可変長引数にすると実装が複雑となることからそのままにしています。

そして、この変更はおそらく既存のコードを壊しません。これまでのassertはそのまま動作し、一部コンパイルエラーを起こしていたものがコンパイル可能となるだけのはずです。

この提案は同時にC標準に対してのものも含んでおり(文書を共有している)、CとC++両方で同時に解決する事を目指しています。

P2265R0 Renaming any_invocable

提案中のany_invocableの名前を変更する提案。

any_invocablestd::functionの制約付きのサブセットであり、最大の特徴はムーブオンリーであることです。しかし、その名前はその特徴を表しておらず、一般的なC++開発者はムーブオンリーstd::functionの名前としてany_invocableを期待することは無いといっても過言ではありません。

また、any_invocableは進行中のfunction_refCallableオブジェクトを所有せず参照するだけの軽量なstd::function)及びstd::functionの補完を行い標準ライブラリの関数ラッパ機能をより完全にするためのものです。しかし、それらとの名前の一貫性が無く、function_refに比べると何をするものなのか分からない命名になっています。

これらの理由により、any_invocableという名前は適切ではないため、変更を推奨する提案です。

この提案ではエンティティの名前に求めるもの、求められるものを6項目上げて説明したうえで、それに従った名前として、名前にfunctionを含めたうえでmovablemove_onlyなどのプリフィックスを付けることが望ましいと述べています(筆者の方はmovable_functionを推しています)。

LEWGのオンライン会議と投票の結果(2021/01/05)、変更後の名前としてmove_only_functionが採用されました。それに伴って、any_invocable(move_only_function)<functional>ヘッダに導入されるように変更されました。P0228はすでにLWGに送られていましたが、これらの変更を反映した上で再送されることになります。

P2268R0 Freestanding Roadmap

C++の機能のフリースタンディング化について、方向性の概説と協力者を募る文書。

検討されフリースタンディングとなるには問題がある機能、有用性があるが未検討で実装経験が必要な機能、現在進行中の機能などについて解説されています。

筆者の方は多くのフリースタンディング関連提案を出していますが、ここに上げられているものを全て実装・テストし提案するにはリソースが足りないため、これらのトピックの実装と提案文書の共著に興味のある人は連絡してほしいとの事です。

P2272R0 Safety & Security Review Board

C++標準化委員会に、安全性とセキュリティのための新しいReview Groupを設立する提案。

モダンC++はsafety criticalなアプリケーション及びセキュアなアプリケーションの分野での優位性を脅かされつつあります。C++が安全でもなくセキュアでもないという認識は、RustやGoなどの他の言語に相対的な優位性をもたらしています。C++11以降、セロオーバーヘッド原則に基づいたC++の進化はC++の競争力の確立に貢献しましたが、C++には安全性とセキュリティに関して行うべきことがまだ残っています。

安全性とセキュリティが強く求められる分野には例えば、航空宇宙、自動運転、ネットワーク、医療デバイスなどがありますが、これらの分野の全てでC++は安全でもセキュアでもないという認識があり、すべての分野でC++と比較してより安全・セキュアなプログラミング言語が存在しています。

このような認識に対処するために、C++標準化委員会は言語標準自体でこれらの問題に対処するために協調して努力する必要があり、そのために専門のReview Groupを設立する、という提案です。

このグループの活動は主に次のようなものです。

  • ほかのグループ(EWGやLEWGなど)から受け取った安全性とセキュリティのための新しい機能の評価を行う。
  • C++の将来の方向性について、安全性とセキュリティの観点からDirection Groupなどのほかグループに助言を行う。
  • 安全性とセキュリティの実際に認識された問題に対処するために、既存の仕様の変更を推奨する。

P2273R0 Making std::unique_ptr constexpr

std::unique_ptrを全面的にconstexpr対応する提案。

C++20で定数式でのnew/deleteが許可され、コンパイル時に動的メモリ確保ができるようになりました。しかし、そこにはstd::unique_ptrはなく、C++11以前の時代の手動メモリ管理を強いられることになります。

std::unique_ptrを定数式で使えるようにすることで、メモリ管理を自動化し、実行時と定数式で同じコード共有できるようになります。

例えば、次のようなコードがコンパイルできるようにするものです。

#include <memory>

constexpr auto fun() {
  auto p = std::make_unique <int>(4); // 今は出来ない

  return *p;
}

int main () {
  constexpr auto i = fun();
  static_assert(4 == i);
}

筆者の方はフォークしたlibc++でこれを実装し、ポインタ比較以外の所では問題が無いことを確認しているそうです。また、std::shared_ptrconstexpr提案も予定しているようです。

P2274R0 C and C++ Compatibility Study Group

前回の会議で決定された、CとC++の共通する部分の相互互換性についてのStudy Group(SG22)を設立するにあたって、WG21とWG14の文化の違いの説明やポリシーを記述した文書。

WG21から見たWG14

  • WG14はWG21と比較して小さな委員会で、会議への出席者は20人程。全ての作業はWorking Groupに分割されず、全体会議で行われる。
  • Study Groupの数も少なめで、SGの活動は常に委員会の会議の外で行われている。
  • WG14での提案の採択は、まず全体会議で提出された提案にフィードバックを行い、著者はそれを受けて再提出する。最終的に、提案は会議で採択されるか、WG14委員会に対する変更への動機付けに失敗するかのどちらか。
  • WG14では、小さなデバイスや特殊なハードウェアにおける実装などニッチなものも含めた実装全体を重視する。
    • WG21では主要な3実装を重視する傾向にある。
  • WG14には委員会の運営原則を記した憲章がある(N2086)。提案には憲章の期待することにどのように応えているかの情報が付加されていることが望ましい。
    • もし提案が憲章に反している場合、なぜその提案には憲章が適用されないのかを論理的根拠とともに述べる必要がある。
  • WG14では実装が少なくとも2つ無いような独創的な提案を採択しない。通常、あるコンパイラのフォークやあまり使用されていないものを複数の実装としてカウントしない。
    • ただし、C++による標準化を1つの実装としてカウントする事を検討中
  • WG21では後方互換性を意図的に壊すことがあるが、WG14では既存のコードが壊れないためにあらゆる手を尽くす。
    • 機能の追加時には予約済みの識別子を用いるように特に注意し、(影響がC++に及び)WG21内で問題を引き起こさない(とみなされるような)場合でも、存在しうる後方互換性の懸念について呼びかけを行う。
  • WG14には標準文書作成を支援するWGはなく、Wordingの議論はオフラインまたはメーリングリストでよく行われる(本会議ではあまり行われない)。
  • 追加のレビューを行うために会議中に提案文書を書き直す事は一般的では無い。WG14委員会は会議のメーリングリストに提出されない提案の議論を行わない事が多いため。
  • C言語の次のリリーススケジュールは2023年になる予定。

WG14から見たWG21

  • WG21は大きな委員会で、本会議には通常250人以上が参加する。出席者の数が多いため本会議ではほとんど作業は行われず、代わりに同じ週に同時に実行される各WGとSGそれぞれで分割して作業される。
    • 4つのWG(EWGとLEWGの2つはC++の進化に焦点を当て、CWGとLWGの2つは標準の表現に焦点を当てている)と多数のSGがある。
  • WG21での提案採択のプロセスは基本的にパイプラインで行われている。提案は多くの場合最初にIncubatorグループ(EWGI,LEWGI)か適切なSGのどちらかで議論が開始される。そこのグループが提案に満足すれば、最も関連性の高いSGかEvolution WG(EWG,LEWG)に移される。そこを通過すると、標準化のWording作成を支援するグループ(CWG/LWG)でチェックされ、最終的に本会議での全体投票にかけられる。
  • WG21には提案の著者が標準に対する文言を作成することを支援するWGがあるため、SG22で見る提案には標準に対する文言が欠けている場合がある。
  • WG21には憲章はないが、委員会の方針と手続き、及びDirection Groupが設定した野心的な目標をまとめた文章がある。これらは有用な背景情報を提供するかもしれない。
  • WG21では、提案の実装経験を非常に価値のあるものだと考えているが、提案を採択するにあたって実装経験に関する要件はない。
  • WG21では、あまり人気の無い実装が最終的にはそれに続くものとして、最も人気のある一部のC++実装における実装経験を重視する傾向にある。
    • WG21が重視する実装には例えば、Clang, GCC, MSVC, EDGが含まれる。
  • WG21は、新機能を追加しようとする際に破壊的変更を制限しようとしているが、ユーザーコードを壊すことが許容される場合にはそれを許可するいくつかの(文書化されていない)ルールがある。これはWG14から来た人にはなじみの無い方法ではあるが、WG21が後方互換性について考慮していることを意味している。
  • C++言語の次のリリーススケジュールは2023年になる予定。現在のWG21のスケジュールはP1000にある。

Discussion