VisualC++ 2010 beta ではラムダ式が使えます。
無名の関数を inline で宣言することが可能で、関数オブジェクトのように
取り扱いできます。
・Download Visual Studio 2010 Beta
・Examples of Lambda Expressions
変数で受け取る場合は auto や template を使った汎用の入れ物が必要です。
auto f0= []( int x, int y ){ return x * y; };
std::tr1::function f1= f0;
lambda 式は無名の型宣言に相当するので、同等の型を明示的に宣言することが
できません。
名前をつけるだけなら下記の定義は出来ますが、これも f0 が有効な範囲のみです。
typedef decltype(f0) lambda_type2; lambda_type2 f2= f0;
全く中身が同じ lambda 式を定義しても別の型と見なされるようです。
auto f2= []( int a, int b ){ return a * b; };
auto f3= []( int a, int b ){ return a * b; };
decltype(f2)* fp2= &f3; // error
上記の例はエラーです。
そのため定義内容を知らない外部から直接参照する手段がありません。
実際に使ってみると単純な関数ポインタでは無いことがわかります。
・lambda 式に応じて無名のクロージャー用クラスが定義される
・メンバとしてキャプチャした変数のコピーまたは参照が格納される
・クロージャークラスのメンバ関数として呼び出される。
確認してみます。
int v= 0;
auto f4= []( int a, int b ){ return a * b; };
auto f5= [&]( int a, int b ){ return a * b + v; };
auto f6= [=]( int a, int b ){ return a * b + v; };
sizeof(f4) == 1
sizeof(f5) == 8 // x64
sizeof(f6) == 4
sizeof(f4) が 1 なのは実質的にサイズが無いことを意味しています。
sizeof(f5) と sizeof(f6) はどちらも v を参照していますが、たまたま x64 で
コンパイルしていたので異なるサイズになりました。
ポインタ(参照)である証拠で x86 だと 4 になります。
auto f4= []( int a, int b ){ return a * b; };
f4.operator()( 2, 10 );
operator() の呼び出しが出来ます。
int v;
auto f5= [&]( int a, int b ){ return a * b + v; };
int r0= f5.v; // cannot access private member
int r1= f5.w; // is not a member
直接アクセスはエラー。変数名はそのままメンバになってます。
lambda 式の定義が返す値はその場でキャプチャされたクロージャーそのものであるということ。
クロージャー未使用でも空の構造体が定義されること。
実行する関数はクロージャー型と静的に関連づけられており、そのために型は常にユニークとなること。
lambda 式の呼び出しは静的なものであってポインタでは無いということ
等がわかります。
実際に出力コードも追いかけて確認。
きちんとインライン展開されているし、これで定義を知らない外部からは直接アクセス
できない理由もわかりました。
外部関数呼び出しのようにインライン出来ない場合は std::tr1::function のような
関数オブジェクトが必要になるわけです。
struct func_base_2 {
virtual int operator()( int a, int b )= 0;
};
template
struct func_impl_2 : public func_base_2 {
T exf;
func_impl_2( T lambda_f ) : exf( lambda_f ) {}
int operator()( int a, int b )
{
return exf( a, b );
}
};
struct func_2 {
func_base_2* ptr;
template
func_2( const T&& lambda_f )
{
ptr= new func_impl_2( lambda_f );
}
template
func_2( T& lambda_f )
{
ptr= new func_impl_2( lambda_f );
}
int operator()( int a, int b )
{
return (*ptr)( a, b );
}
~
};
func_2 f7( []( int x, int y ){ return x*x + y*y; } );
int mm= f7( 3, 9 );
この場合の f7 は callback として持ち出せます。
使えるのはクロージャーが作られた関数スコープの中のみです。
関連エントリ
・VisualStudio 2010 beta1