C++11
メタプログラミングとは、他のプログラム(もしくは自分自身)を生成、修正するプログラムを生成することである。これらの動作は、コンパイル時、もしくは実行時に発生する。C++標準化委員会は、テンプレートを通したコンパイル時メタプログラミング(テンプレートメタプログラミング)を可能にするライブラリの導入を決定した。
以下は、現在の標準でのメタプログラミングの例である: 指数計算に、テンプレート実体化の再帰を用いている。
多くのアルゴリズムは、型の違うデータに適用可能である。C++のテンプレートはジェネリックプログラミングをサポートし、より小さく有用なコードを作れる。それでも、アルゴリズムは使用されている型の情報を必要とすることも多い。この種の情報は、型特性を使用してテンプレートクラスの実体化時に展開できる。
型特性は、オブジェクトの分類やクラス(または構造体)の性質を示すものである。新ヘッダの<type_traits>で宣言される。
次の例は、関数テンプレートelaborateの例である。この関数は、渡されたデータ型に基づき、二つのアルゴリズム (algorithm.do_it) から一方を実体化する:
ヘッダ<type_transform>で定義された型特性を用いると、型変換の処理を生成することも出来る(static_castとconst_castはテンプレート中では不十分)。
この種のプログラミングで、美しく簡潔なコードを書ける。しかし、このテクニックの弱点はデバッグである。コンパイル時のデバッグは不適切であるし、プログラム実行時には非常に難しい。
C++ 標準では、特に std::sort() や std::find() といった C++ 標準ライブラリのアルゴリズム関数と組み合わせた時に、アルゴリズム関数の呼び出しの近くで述語関数を定義したいと思う機会が多い。しかし、このためには関数内部でクラスを定義するしかなかった。この方法は記述量が多く、またコードの流れを妨げがちである。加えて、標準 C++ の規則では、関数の中で定義されたクラスをテンプレートの中で使うことを認めていないので、結局どうしても使うことは不可能であった。
これに対して、C++11ではラムダ関数が定義できるようになった。
ラムダ関数は以下のように定義される:
これは、int型の引数を二つとり、int型の値を返すラムダ式である。その内容は{ } の中に記述される。 ラムダ関数の内容が "return 式" の形式である場合には、戻り値型を省略することができる。
このラムダ関数の戻り値型は decltype(x+y) になる。
ラムダ関数が値を返さない場合、つまり戻り値型が void の場合も戻り値型を省略できる。
ラムダ関数の外で定義された変数であっても、ラムダ関数の中で使用することができる。この種のものは一般にクロージャと呼ばれる。クロージャは[ ]の中に記述する。
これは、リストの全要素の合計を表示する例である。 変数 total がラムダ関数のクロージャの一部として格納される。これはスタック変数 total への参照なので、代入することで外側の total の値も変更される。
& は参照を表すシンボルである。スタック変数に対応するクロージャ変数は、&を付けずに定義することも可能で、その場合はラムダ関数は値をコピーする。
C++標準化委員会はマルチスレッドの標準的サポートの導入を計画している。
これには2つの側面がある。つまり、複数スレッドが1つのプログラム中で共存できるメモリモデルを定義することと、スレッド間相互作用のサポートを定義することである。2つ目に関してはライブラリによって提供される。#スレッディングを参照のこと。
複数のスレッドが同じメモリ位置にアクセスする可能性がある環境下でのプログラム記述には、メモリモデルが必要となる。つまり、ルールを遵守するプログラムは正しく実行されるであろうと思われるが、ルールに従わないプログラムはコンパイラの最適化に依存する未定義の振る舞いや、memory coherenceの問題を抱えることになる。
これにより、スタック変数を参照するのかコピーするのかどちらの意図があるのかわかるようになる。スタック変数を参照することは危険な場合がある。例えば (C++11 標準の) std::function オブジェクトに格納するなどして、ラムダ関数を生成したスコープの外側で使いたい場合は、ラムダ関数がスタック変数を参照していないことをよく確認する必要がある。
ラムダ関数がその定義のあるスコープの内側で実行されることが保証されている場合は、次のように記述すれば、スタック変数をひとつひとつ指定せずに全ての利用可能なスタック変数を使用できる。
[ ]の中には次のような指定が可能である。
また、ラムダ関数がクラスのメンバ関数により定義された場合、そのクラスのフレンドであると見なされる。そのようなラムダ関数は、そのクラス型のオブジェクトへの参照を使って、内部のメンバにアクセスできる。
この例は、このラムダ関数の生成するスコープが SomeType のメンバ関数の中にある場合にのみ動作する。
this ポインタは、各時点でメンバ関数が作動しているオブジェクトを指しているが、その扱いは特別である。ラムダ関数中に明示的な指定が必要になる。
[&] または [=] 形式のラムダ関数を用いていれば、thisの記述は必要ない。
ラムダ関数の型はコンパイラ依存であり、明示的に記述することはできない。ラムダ関数を引数として取得したい場合は、その型をテンプレート型にするか、std::function などの型消去の仕組みを使う必要がある。auto キーワードを使ってラムダ関数をローカル変数に格納することはできる。