C++0xなるもの

が策定されている模様。C++11 - Wikipediaで読んでいたんですが、この中でtomityが気になる項目をピックアップしてみました。


なお、このエントリは引用の範囲を超えてるのでWikipediaのライセンスに従い、このエントリはGFDLライセンスとなります。

並列実行

未だ提案段階ではあるが、スレッドを用いた並列実行 (cobegin/coend)機構が次期標準(もしくはその先の規格)に導入されるかもしれない。

この提案が導入されたとき、どんなキーワードが割り当てられるかは重要ではない。むしろ、並列実行用の命令ブロックを非常に簡単に実装できることを示す方が重要だろう。


これは高速化をする上で重要な仕様変更ですね。

非同期関数呼び出し

他の提案として、Futureの考え方を用いた、非同期な関数的な呼び出しの実装機構の提案がある。

この構文は以下のようなものになるであろう:

これはどうなんだろう。Ajaxっぽいな。

コンストラクタ間の委譲

現在のC++では、コンストラクタは他のコンストラクタを呼ぶことは出来ず、各コンストラクタでクラスメンバの初期化を全て行わなくてはならない。これはしばしば、初期化コードの重複を招く。

C++0xではコンストラクタが他の同等なコンストラクタを呼び出すこと(委譲)が出来るようになる予定である。これにより、コンストラクタが他のコンストラクタを最小のコード追加で利用できるようになる。Javaのように、他の言語では既にこれを取り入れている物もある。

これは便利

コンストラクタの継承

現行のC++において、基底クラスから派生クラスが創られるとき、両者のコンストラクタ集合は全くの別物として扱われる。派生クラスは基底クラスのコンストラクタを呼び、またパラメータを渡すこともできるが、各コンストラクタをユーザーが全く別に作らなくてはならない。これは機械的な作業であり、ユーザー・エラーを起こしやすいものであった。

C++0xではクラスでコンストラクタの転送を指定することが出来るようになる予定である。これにより、コンパイラは呼び出しの転送を行うコードを生成することになる。注意すべき点は、この機能は、全てのコンストラクタ呼び出しを基底クラスに転送するか、全く転送しないか、の二者択一の機能であることである。他の注意点として、多重継承時の制限もある。同じシグネチャを持つ二つのコンストラクタに転送することは出来ないし、転送先のコンストラクタと同じシグネチャを持つコンストラクタを後で宣言することはできない。

これは便利

メンバ初期化子

現行の標準C++では、クラスのメンバはコンストラクタでのみ初期化されることになっている。C++0xでは、メンバの初期化に以下のような構文を認める予定である:

これは便利

インスタンス化されていないクラスのメンバへのsizeof

現行の標準C++では、sizeof演算子は型とオブジェクトに対してしか適用できない。そのため、以下のようには使用できない:

これはコンパイル時プログラミングをする時楽になりそう。

コンパイラが生成する関数へのdefault/delete指定

現行の標準C++では、オブジェクトにコンストラクタ、コピーコンストラクタ、代入演算子 (operator =)、そしてデストラクタが与えられていない場合、コンパイラが自動的にそれらを提供する。ユーザーは自身のバージョンを定義することで、デフォルトの動作を上書きすることができる。また、C++は全てのクラスに適用されるグローバルな演算子(operator ,やoperator new)も提供しており、その動作もユーザーが上書きできる。

しかし、デフォルトで作られる関数の生成を制御する方法が非常に少ないという問題がある。例えば、クラスを実質的にコピー不能にするには、コピーコンストラクタと代入演算子をprivateとして宣言し、空の実装を行うことによって実現できる。コピーしようとしてこれらの関数を使用しようとすると、コンパイラやリンカがエラーを報告するようになるというわけである。しかし、これは理想的な解法とはいえない。

例えばデフォルトコンストラクタについて考えると、コンパイラに対して明示的に生成するよう伝えておくことも有用である。オブジェクトに「全く」コンストラクタが定義されていないのであれば、コンパイラはデフォルトコンストラクタを生成しない。これは多くの場面で有用であるが、特別なコンストラクタとコンパイラが生成するデフォルトを両方持っておくことも有用な場面がある。

C++0xでは、これらコンパイラが生成する関数の使用・不使用を明示できるようになる予定である。例えば、以下のクラスはデフォルトコンストラクタの使用を明示している:

C++のクラス定義、特にコピーコンストラクタとか、演算子オーバーロードのあたりのコンパイルエラーの表示が分かりにくくて困ってたんですよね。これは助かる。

初期化リスト

現行のC++標準では、初期化リストの考え方をCから拝借している。これはつまり、構造体のメンバ定義順の引数リストを用いて、構造体や配列を生成できるようにするものである。初期化リストは再帰的に適用されるので、構造体の配列や構造体を含む構造体にも用いることができる。静的なデータの初期化や構造体を特定の値に初期化したいときなど、初期化リストは有用なものである。しかしこの便利な機能は全ての面で使える訳ではない。今のC++では Plain Old Data (POD) 型と確認された構造体・クラスにしか前述の機能は適用できない。非PODクラスには適用できず、std::vectorやboost::arrayといったC++スタイルの有用なリスト型にも使えないのである。

C++0xでは、あらゆるオブジェクトを初期化リストで構築できるようになる予定である。この機能は、「シーケンス・コンストラクタ」の定義を通して与えられる。以下がこの形式のコンストラクタである:

これは便利。テストケース作るとき重宝しそう。

可変長引数テンプレート

現行の標準C++のテンプレート(クラステンプレート・関数テンプレート)は、あらかじめ決められた個数の引数しか取ることができない。C++0xでは、テンプレート定義の際にあらゆる型の引数を任意数取れるようになる予定である:

これは便利。同じ関数をオーバーロードしまくって無理やり再現してたもんな。

コンセプト

C++では、テンプレートクラス・関数は引数とする型に必然的に何らかの制限を課す。例えば、STLコンテナは格納する型にデフォルトコンストラクタを要求する。Foo&型のオブジェクトを受ける関数にはFooのあらゆるサブタイプを渡せる、というようなクラス階層によって提供される動的な多態とは違い、テンプレート引数には、テンプレートが使う演算をサポートするクラスならば何でも渡すことが出来る。しかし、通常の関数の場合は引数に対する要求は明確である(先の例なら、Fooのサブタイプであること)が、テンプレートの場合は、オブジェクトが持たなければならないインターフェースはテンプレート定義の中に隠れてしまっている。コンセプトはテンプレート引数が持たなくてはならないインターフェースを明文化する機構を提供する。

コンセプトを導入する理由の一つは、エラーメッセージの質の改善である。テンプレートが必要とするインターフェースを持っていない型をプログラマが使おうとした場合、コンパイラはエラーを出す。しかし、その種のエラーは理解しがたいものになりやすく、特に初心者には非常に難解である。この理由としては、エラーメッセージにテンプレート引数が省略されずに表示され、非常に長いエラーメッセージが出力されてしまうことが多いことが挙げられる。コンパイラによっては、単純なミスが数キロバイトものエラーメッセージを出す結果になることもある。他の理由として、エラーメッセージがエラーの実際の理由を明確に示していないことがある、ということもある。例えば、コピーコンストラクタを持たないオブジェクトのvectorを構築しようとした場合、大抵の場合のエラーメッセージは「内包するオブジェクトにコピーコンストラクタを呼んでしまったvectorクラス」に言及するものになってしまう。熟練したプログラマでないと、本当のエラーが「vectorに渡した型がvectorの要件を満たしきれていないこと」が解らないだろう。

この問題を解決するため、C++0xでは言語にコンセプトの機能が追加された。コンセプトは、名前付きの構造であり、型が提供しなければならない機能を指定する。この点は、オブジェクト指向プログラミングで、型の行えることの制限の定義を基底クラスにより行うのに似ている。しかしオブジェクト指向プログラミングとは違い、コンセプトの定義自体にはテンプレートに渡される型が明示的に関連付けられず、テンプレート定義の側で結びつけられる:

これは超いい。コンパイル時プログラミングをしている時、非常に長いエラーメッセージに悩まされ、そのメッセージも的を外れててバグの特定に苦労すること多かったんですが、こいつのおかげでエラーメッセージを適切に出せるようにはなりそうです。重要な仕様変更になりそう。

山括弧

テンプレートを用いた総称プログラミングを導入するのに、新形式の括弧を導入するのが必要であった。そのためC++には、丸括弧"()"、角括弧" []"、中括弧"{}"に加えて、山括弧"<>"が導入された。しかしこれにより字句的曖昧さが生じ、(プログラマの意図とは違う、という意味で)間違って解析され、構文エラーになるという事態が発生した:

typedef std::vector<std::vector<int> > Table;  // OK。
typedef std::vector<std::vector<bool>> Flags;  // エラー! ">>"は右シフトと解析される。
 
void func(List<B>= default_val);  // エラー! ">="は比較演算子と解析される。
void func(List<List<B>>= default_val);  // エラー! ">>="は右シフト代入と解析される。
 
template<bool I> class X {};

X<(1>2)> x1;  // OK。
X< 1>2 > x1;  // エラー! 一つ目の">"は山閉じ括弧と解析される。

C++0xの字句解析では、最も深くネストした開き括弧が山括弧"<"である場合、">"は、次に">"や"="が続いていても山閉じ括弧として扱われる。これにより、上記のエラーは最後のものを除いて修正される。最後のものを修正するには、曖昧さを取り除くために丸括弧を足さなくてはならない。

コンパイル時プログラミングがやりやすくなりますね。

ラムダ関数とラムダ式

C++標準では、特にsortやfindといったC++標準ライブラリのアルゴリズム関数と組み合わせるなどの用途で、アルゴリズム関数呼び出しの近くで述語関数を定義したいという機会が多い。しかし、このために言語に用意された仕組みは一つしかなく、関数内部でクラスを定義する方法だけである。この方法は厄介で長ったらしく、コードの流れを妨げがちである。

簡明な解決策は、ラムダ式とラムダ関数の定義を認めることである。C++0xでは、ラムダ関数が定義できるようになる[]

STLが使いやすくなりますね。C++本来の使い方を推奨する上で重要な仕様変更だと思います。

型推論

現行の標準C++(そしてC)では、変数の型は使用に際して明示的に指定されねばならない。しかし、テンプレート型やテンプレートメタプログラミングなどにおいては、特に関数の戻り値型が複雑に定義されているような場合、型を簡単に書き下せない。そのような場合には、中間結果を変数に格納することが難しく、メタプログラミングライブラリの内部仕様を知っていなくてはならなくなる場合もある。

C++0xではこの制約を軽減する方法が二つ提供された。一つ目の方法は、明示的に初期化される変数の定義にautoキーワードを使う方法である。これにより、初期化子によって変数の型が特定される:

さらに、キーワードdecltypeによって、式の型をコンパイル時に決定することができる。

これは便利。型がきっちり決まっているのは良いことなんですが、コンストラクタの呼び出しと書き方がほとんど変わらないので、必要ないんじゃないかなとか思ってました。

範囲ベースのforループ

Boost C++ライブラリでは、いくつか「範囲」のコンセプトを使用しているものがある。範囲は、リストの二点をもってリストを表現するものであり、コンテナにも似ている。順序付きのコンテナは範囲コンセプトから見れば上位にあり、順序付きコンテナから二つイテレータを持ってくれば範囲を定義できる。これらの考え方や、またこれを元に動作するアルゴリズムが、C++0xの標準ライブラリに組み込まれる予定である。しかしC++0xでは、ライブラリだけではなく、範囲コンセプトを使う言語機能ももたらされることになる予定である。

for文では、範囲の考え方を用いて簡単な反復が行えるようになる:

int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array)
{
  x *= 2;
}

STLが使いやすくなりそう。

タプル型

タプルとは、決められた次元数の、異なる種類の型のオブジェクトから構成されたコレクションである。あらゆる型のオブジェクトが、タプルの要素になりうる。

これってModern C++ Design―ジェネリック・プログラミングおよびデザイン・パターンを利用するための究極のテンプレート活用術 (C++ In‐Depth Series)で提唱されていたTypeListですよね。コンパイル時プログラミングが楽になりますね。

正規表現

正規表現のために作られた、多くの非標準、準標準のライブラリがある。このようなアルゴリズムを使うのは非常に一般的なので、オブジェクト指向言語の能力を用いて、標準ライブラリにもこの機能が導入される。

これはBoostのregexライブラリを持ってきた形なんでしょうか。




ほとんど全ての変更は使いやすくなるためのものですね。覚えなければならないテクニックが減ってC++を使いこなせるようになるのが楽になりそうです。良きことかな。

このエントリにおけるテキストの複製、配布及び改変は、Free Software Foundationが発行するGNU Free Documentation Licenseのバージョンの1.2以上の条件の下に、許諾されます。変更不可部分 (Invariant Sections)、表表紙テクスト (Front-Cover Texts) 及び背表紙テクスト (Back-Cover Texts) はありません。