Trip Report: June 2008 ISO C++ Standards Meeting

Herb Sutter "Trip Report: June 2008 ISO C++ Standards Meeting"の適当訳です。
内容的にはid:faith_and_braveさんとこで既に日本語で解説済みのものばかりかも。


ISO C++標準委員会が6月6〜14日にフランスのソフィア・アンティポリスで開催された。議事録はここで読める(これがカバーしているのは全体セッションだけで、私が主に参加していた個々のテクニカルセッションは含んでいない)。

以下は、関係する論文へのリンクつきの議論のまとめと、次回会議の情報である:

要注目:C++0xの完全なドラフトは9月に登場

今回の会議における最大のゴールはC++0xの機能的に完全なドラフトを作り、国際レビュー&コメント(ISOでは「公式委員会草稿」またはCDと呼ぶ)のための完全な公開ドラフトの発行をこの九月に行う、ということだった。我々はこの方向で動いているところなので、秋には、世界中の人々がC++0xのカタチを詳細に知ることができるようになるだろう。

また、国際コメントレビューを一回ではなく二ラウンド開催する予定だ。第一ラウンドは2008年9月の会議の後に開始し、第2ラウンドはおそらくその一年後になる。こうすることで世界中にこの標準をよく調べる機会と、各国団体がコメントする機会を与える。ただ、2008年9月のCDは「上述のモノ」、つまり機能的に完全なC++0xとなる。CDから最終的な国際標準への変更点はバグ修正と明確化だけになる予定だ。だから、CDを機能的に完全なベータ版だと考えてもらえると助かる。*1

6月の会議までに、我々は完全に近いC++0x国際ワーキングドラフトを作成していた。C++0xの一部となるほとんどの機能は既に「チェックイン」済み。初期化子リスト、Rangeベースforループ、conceptなど若干が投票に掛けられるレベルに固まるのを待っているところだ。

これらのうち、conceptが一番厄介なC++0xの機能だ。これが標準C++に追加しようとしているもっとも大きな新機能であることを考えれば驚くにはあたらないが。そんなわけで、6月の会議での主な目的は可能な限りconceptに関して作業を進めることにあった。我々はほとんどやり遂げた。このフランスだけでなく、この冬から春に掛けて行われたもっと小さな会議も含めて、多くの作業に対して感謝したい。今回初めて、concept標準の文言に関する既知の問題や異論を残さず会議を終えることができた。これで次の9月の会議でconceptをワーキングドラフトに「チェックイン」できそうだ。このことは、完全なドラフトの発行への道筋が明確になったということでもあるし、標準を完全な体裁に「焼き上げる」のに十分なレビューを一回でなく2ラウンド行うという計画の動機にもなった。

さて次に、この6月の会議で投票された主な機能についてまとめていこう。

初期化子リスト

C++0xの初期化子リストは二つの目的を成し遂げるためのものだ:

  • 一貫性:特にテンプレートを書く場合に役立つ、一貫してどこにでも使える単一の形態の初期化文法を提供する
  • 利便性:あらゆる種類の型、殊にコンテナ、に対してCの初期化子文法を用いる汎用の方法を提供する

動機と元のデザインについて詳しく知りたいのであればN2215を、最終的に標準に含まれたものはN2672およびN2679を参照。

例:集約型とクラスの初期化

集約型を以下のように初期化できるのは便利だ:

struct Coordinate1 {
  int i;
  int j;
  //…
};

Coordinate1 c1 = { 1, 2 };

しかし(コンストラクタを持つ)クラスに対する文法はだいぶ異なる:

class Coordinate2 {
public:
  Coordinate2( int i, int j );
  // …
};

Coordinate2 c2( 1, 2 );

C++0xでは、上述の書き方も可能ではあるが、初期化子リストを用いて、あらゆる種類の型を初期化する標準的な方法が加わる:

Coordinate1 c1 = { 1, 2 };
Coordinate2 c2 = { 1, 2 }; // legal in C++0x

単一形式の初期化文法を持つことで、特にテンプレートのコードを書く場合に役に立つ。テンプレートが簡単に幅広い種類の型に対して動作できるようになるからだ。

例:配列とコンテナの初期化

単一形式の初期化が欠けていることで殊更憂鬱になる箇所がある…少なくとも私は、記事や発表で見せるようなコードを試すためにテスト・ハーネスを書く時にこれを感じるのだが…つまり、コンテナをデフォルト値で初期化するときのことだ。何らかの既知の値で初期化されたコンテナを作りたいと思った時、配列なら:

string a[] = { “xyzzy”, “plugh”, “abracadabra” };

と書けるのに、vectorなどのコンテナではデフォルト・コンストラクトして各エントリを個別にpushしなくちゃいけない:

// Initialize by hand today
vector<string> v;
v.push_back( “xyzzy” );
v.push_back( “plugh” );
v.push_back( “abracadabra” );

あるいは、もっと腹が立つやり方として、利便性のためにまず配列を初期化してからそのコピーでvectorを構築したりする:

// Initialize via an array today
string a[] = { “xyzzy”, “plugh”, “abracadabra” }; // put it into an array first
vector<string> v( a, a+3 ); // then construct the vector as a copy

こういうの嫌いでしょう?

配列はほとんどの使い方でコンテナに比べて劣るから、単に古のCの時代から言語に組み込まれていたというだけでこの独特の利便性を持っていることにはウンザリする。

簡便な初期化の欠落でもっとイライラさせられるのはmapだ:

// Initialize by hand today
map<string,string> phonebook;
phonebook[ "Bjarne Stroustrup (cell)" ] = “+1 (212) 555-1212″;
phonebook[ "Tom Petty (home)" ] = “+1 (858) 555-9734″;
phonebook[ "Amy Winehouse (agent)" ] = “+44 20 74851424″;

C++0xでは、配列と同じように簡便に、既知の値を持ったコンテナを初期化できるようになる:

// Can use initializer list in C++0x
vector<string> v = { “xyzzy”, “plugh”, “abracadabra” };
map<string,string> phonebook =
  { { “Bjarne Stroustrup (cell)”, “+1 (212) 555-1212″ },
    { “Tom Petty (home)”, “+1 (858) 555-9734″ },
    { “Amy Winehouse (agent)”, “+44 99 74855424″ } };

さらにオイシイことに、単一形式の初期化文法は配列も扱いやすくしてくれる。例えば、配列初期化子の文法は動的にアロケートされた配列やクラスメンバの配列をサポートしていなかった:

// Initialize dynamically allocated array by hand today
int* a = new int[3];
a[0] = 1;
a[1] = 2;
a[2] = 99;

// Initialize member array by hand today
class X {
  int a[3];
public:
  X() { a[0] = 1; a[1] = 2; a[2] = 99; }
};

これらのケースも、C++0xの配列初期化文法では一貫して可能になる:

// C++0x
int* a = new int[3] { 1, 2, 99 };

class X {
  int a[3];
public:
  X() : a{ 1, 2, 99 } {}
};

さらなる並列プログラムのサポート

昨年の10月に、既にstate-of-the-artなメモリモデル、アトミックな処理、スレッドパッケージを採択していた。これらは、我々がこの標準に含めたかった主な項目をカバーしていたが、まだ若干進行中のものが残っていた。以下は並列プログラムに関して今回行われた主な変更点である:

スレッド・ローカルな記憶域(N2659)
thread_localストレージ指定子を用いることで、スレッドごとに1回だけインスタンス化される変数を宣言できる。例えば:
class MyClass {
  …
private:
  thread_local static X tlsX;
};
void SomeFunc() {
  thread_local static Y tlsY;
  …
};
並列プログラムにおける動的な初期化とデストラクト(N2660)
これは主に二つの主な事例を扱っている:
  • staticおよびグローバルな変数は、main()関数の開始前に複数のスレッドからアクセスしようとした場合、並行的に初期化・破壊される。ただし、二つ以上のスレッドがその変数を並列的に初期化できる(または利用できる)のであれば、そのアクセスを同期制御するのはユーザの責任である。
  • 関数ローカルなstatic変数の初期化は自動的に保護される。すなわち、あるスレッドがその変数を初期化している間にその関数に侵入し、その変数宣言に到達した他のスレッドは、その初期化が完了するまで実行を停止される。したがって、初期化の競合や初期化と利用の競合(initialize-use race)をガードする必要はなく、またもしその変数が一度構築されたら不変であるのなら、同期処理をまったく必要とせず安全に使うことができる。その変数が構築後に書き換え可能である場合、初期化後の変数利用の競合(use-use race)に関しては従来どおり同期処理を用意する必要がある。
標準ライブラリに関数するスレッド安全性の保証(N2669)
これはつまり「vector<T> v や shared_ptr<U> sp をスレッド安全に利用するために必要なことは何ですか?」といった問いに対して、明示的に「他のオブジェクトと同じ。つまり、v や sp が共有されていると分かっているなら、それらへのアクセスは同期処理を入れましょう」と回答したということ。ほんの少数のオブジェクトについては、内部の同期処理について保証してやる必要がある。グローバル・アロケータはそうしたものの一つなので、より強い保証を与えられることになった。

以下の「その他の機能」セクションにも並列プログラムに関連したもう少し些細な項目が含まれている。

追加のSTLアルゴリズム (N2666)

以下の新しいSTLアルゴリズムが追加された。いくつかは穴を埋めるためのもの(例えば「どうしてcopy_ifや個数限定版のhogeアルゴリズムがないの?」)で、後はお手軽な拡張(「なんでall_ofとかany_ofとか無いの?」)だ。

all_of( first, last, pred )
範囲内のすべての要素が pred を満たすとき、またその場合に限り、真を返す
any_of( first, last, pred )
範囲内のいずれかの要素が pred を満たすとき、またその場合に限り、真を返す
copy_if( first, last result, pred)
「なんでこれ標準に無いの?」の見本のようなアルゴリズム
copy_n( first, n, result)
n 個分のコピー
find_if_not( first, last, pred )
pred を満たさない最初の要素へのイテレータを返す
iota( first, last, value )
範囲内の各々の要素について value を代入して「++valueするように」valueをインクリメントする
is_partitioned( first, last, pred )
範囲内の要素が pred によって分割されているとき、またその場合に限り、真を返す。つまり、predを満たすすべての要素は満たさない要素より前に現れている場合に真
none_of( first, last, pred )
範囲内のいずれかの要素も pred を満たさないとき、またその場合に限り、真を返す
partition_copy( first, last, out_true, out_false, pred )
pred を満たす要素を out_true に、満たさない要素を out_false に、それぞれコピーする
partition_point( first, last, pred)
範囲内の要素が pred によって分割済みである(上述のis_partitionedを参照)と仮定し、predを満たさない最初の要素へのイテレータを返す
uninitialized_copy_n( first, n, result )
n個分の uninitialized_copy

その他に承認された機能

  • N2435 スマートポインタに対する明示的なbool
  • N2514 アトミックに対する暗黙の変換オペレータ
  • N2657 テンプレート引数としてのローカルで無名の型
  • N2658 ラムダ関数のconst性
  • N2667 POSIXに対する予約名前空間
  • N2670 ガベージコレクションと到達可能性ベースのリーク検出に関する最小限のサポート
  • N2661 時刻と期間のサポート
  • N2674 shared_ptrへのアトミックなアクセス
  • N2678 第30章(スレッド)に対するエラー処理仕様
  • N2680 標準コンテナに対する位置指定インサート

次回会議

ついさっきフランスから戻ったばかりだが、次のISO C++標準委員会のミーティングは既に間近に迫っている:

会議は公開されているので、ご近所の方はお気軽にご参加ください。

*1:要するにコメントによって大幅な変更を受け入れるってことはないよ、ってこと?