Trip Report: October 2007 ISO C++ Standards Meeting

Herb SutterのTrip Report: October 2007 ISO C++ Standards Meetingの抄訳。
#我ながら懲りないな…

10月1日〜6日に掛けて、ISO C++標準委員会がコナで開催された。

C++0xに何が入るのか、そしてそれはいつか?

既にブログにも書いている(最近だとこことかここように、委員会は非公式にC++0xと呼ばれているC++標準の最初のメジャーリビジョンの作業中である。ライブラリと同様、言語それ自体も待ち望まれていた多くの仕様変更や機能拡張が含まれている。

委員会は2009年に技術的な作業を完了すると決断した。2008年9月に機能的に完備されたドラフトを発行し、翌年はフィードバックを受けてのチューニングに費やす予定。

これは今年の初めにブログに書いたスケジュールから1年ずれ込んでいる点に注意してほしい。なぜずれ込んだのか?2,3の大きな機能がまだワーキングペーパーに「チェックイン」されておらず、それらはこのリリースへのlogn polesになっている。特に注意すべきは以下の3点:

  • コンセプト(エラーメッセージ、オーバーロード、その他の一般的なテンプレートの使い勝手を改善する、テンプレートへの制約を記述する方法)
  • 先進的な並列プログラムライブラリ(例:スレッドプール、reader-writer lock)
  • ガベージコレクション

おそらく今回の会議で最も大きいことは、時間切れも構わないと決断したことだ。我々はコンセプトなしでC++0xを出荷することはしない。ただし、他の2点(のどちらかあるいはどちらも)がなくても出荷することもありうるし、おそらくそうなる。

並列プログラム
今回は並列プログラムに関する転換点となった。大量の並列プログラムに関する拡張がワーキングドラフトに取り込まれ、出発点に立った。
  • メモリモデル
  • atomicライブラリ
  • 基本的なスレッド、ロック、条件変数

そして、基本的にここで止まることにした。我々はまだ非同期 future型をC++0xに追加する予定だが、しかしスレッドプールのような機能はこの標準の後に延期された。

ガベージコレクション
C++0xに対してガベージコレクションの明示的なサポートは追加しない予定だが、これを実装する際に問題になるような阻害問題(pointer hidingなど)を除去する方法を探ることだけ考えている。具体的には以下が期待されている:
  • C++0xはいくつかの隠蔽されたポインタの使い方を未定義とする予定である。そしてこの制限から特定のオブジェクトを免除し、ポインタフリーなメモリ領域を指定するための少数の関数セットを提供する(これらの関数はnon-collected confirming implementationでは瑣末な実装になるだろう)。

C++0xドラフトに採択した項目

今回の会議でC++0xワーキングドラフトに採択された主な機能は以下のとおり:

nullptr (N2431)

C++/CLI から導入した拡張。
null ポインタを表すのに、不幸にオーバーロードされたリテラルの 0(や、そのマクロ表記であるNULL)を用いるのではなく、nullptr と明示的に書ける。従来の方法では null なのか整数のゼロなのか判別することは困難だった。この提案は私(Herb Sutter)とBjarneによる。

明示的な変換演算子 (N2437 and N2434)

明示的に呼び出された際に、コンストラクタの変換がどのように行われているか知っているだろうか?

    class C {
    public:
      C( int );
      explicit C( string );
    };

    void f( C );

    f( 1 ); // ok, Cからintへの暗黙の変換
    f( "xyzzy" ); // error, 文字列リテラルからCへの暗黙の変換はない
    f( C("xyzzy") ); // ok, Cへの明示的な変換

しかしC++には暗黙的な変換を書くための二つの方法がある。一つは、他の型「から」変換するための、上記のように引数が一つだけのコンストラクタ。もう一つは、以下のように他の型「へ」変換するための変換演算子である。これからは後者も明示的に書けるようになる:

    class C {
    public:
      operator int();
      explicit operator string(); // <-- 新機能
    };

    void f( int );
    void g( string );

    C c;
    f( c ); // ok, Cからintへの暗黙の変換
    g( c ); // error, Cからstringへの暗黙の変換はない
    g( string(c) ); // ok, stringへの明示的な変換

これで対称的になった。クールだ。C++標準ライブラリ自身でこの機能がどのように用いられているかはN2434を参照。

並列メモリモデル (N2429)

「タダ飯は終わった(The Free Lunch Is Over)」にも書いたように、チップデザイナとコンパイラ作者は「より速いCPUを提供せよという、非常に強い圧力の下に居る。あなたのプログラムをより速く走らせんがために、そうしたCPUは、あなたのプログラムの意味を変え、恐らくは壊してしまう、という危険を冒すことになる。」これはマルチコアやマルチプロセッサの下ではより悪くなるだけだ。

メモリモデルは恐らく、プログラマオプティマイザの間の最も低レベルの契約であり、より高いレベルの並列プログラミング作業の礎になるものだ。私のメモリモデルに関する論文から引用しよう:「メモリモデルとは、(a)プログラムオーダーに対して、メモリ読み書きが、プロセッサによってどのように実行されるか、および(b) あるプロセッサによる書き込みが、他のプロセッサからどのように見えるか、を記述したものである。どちらの側面もコンパイラ、物理プロセッサ、およびキャッシュによって行われる最適化の有効性に影響する。故にメモリモデルの鍵となる役割は、プログラム容易性(プログラマへのより強い保証)とパフォーマンス(プログラムのメモリ操作順序変更に対するより大きな柔軟性)のトレードオフを定義することになる。」

Atomic型 (N2427)

メモリモデルと深く関係するのが atomic 型の機能である。これはロックなしで安全に並列的に使える。C++0xでは "atomic" のように記述する。
以下はこの機能を用いた、Widgetシングルトンの実装における、正しい(そう、正しい!)Double-Checked Lockingの例である:

    atomic Widget::pInstance = 0;

    Widget* Widget::Instance() {
      if( pInstance == 0 ) { // 1: 第1のチェック
        lock hold( mutW ); // 2: ロックを確保(クリティカルセクションに突入)
        if( pInstance == 0 ) { // 3: 第2のチェック
          pInstance = new Widget(); // 4: 生成と代入
        }
      } // 5: ロック解放
      return pInstance; // 6: ポインタを返す
    }
スレッドライブラリ (N2447)

上の例で lock を使っているのに気がついただろう。これらも標準ドラフトに含まれている。C++0xは、スレッド、さまざまな種類のミューテックスやロック、条件変数をサポートする。さらに、並列プログラムの助けになるその他の便利な機能も。例えば効率的で移植性のある std::call_once など。

その他の採択された機能
  • N2170 "Universal Character Names in Literals" {参照:ユニバーサルキャラクタ名}
  • N2442 "Raw and Unicode String Literals; Unified Proposal (Rev. 2)"
  • N2439 "Extending move semantics to *this (revised wording)" {メンバ関数の指定でconst&とかconst&&とつけることで、thisをlvalue-ref/rvalue-refに切り替えられる。一見するとすごく気味が悪い…}
  • N2071 "Iostream manipulators for convenient extraction and insertion of struct tm objects" {Apache C++ Standard Libraryから。これは便利!strftime/strptime相当の処理をiomanipでお気楽に書ける}
  • N2401 "Code Conversion Facets for the Standard C++ Library" {codevt_utf8, codecvt_utf16, codevt_utf8_utf16の3つ。それぞれ UTF8 -> UCS2/4, UTF16 -> UCS2/4, UTF8 -> UTF16}
  • N2440 "Abandoning a Process" { atexitよりさらに後に実行されるat_quick_exitを追加 }
  • N2436 "Small Allocator Fix-ups"
  • N2408 "Simple Numeric Access Revision 2" {atoiのstring版であるstoiなど、lexical_castより優雅さに欠けるが効率よさそうな関数群}

今後の会議予定

これらの会議は公開されているので、お近くにいらっしゃるならお気軽にご参加ください。