日別アーカイブ: 2013年6月1日

C++11 Lambda function ラムダ関数

C++11 では関数の中で関数オブジェクトの宣言ができるようになっています。
スクリプト系言語などでもよく見る書き方で、下記は関数を値として代入しています。

void func_test()
{
  auto func_obj= []( int a ){
    return a + 100;
  };

  func_obj( 23 );
}

関数ポインタとの違いは、関数や式の中に実体をインラインで無名宣言できることと、
実行コードだけでなく、オブジェクトの中にデータ領域も併せ持つことです。
要するに関数オブジェクトで、以前だと下記のような書き方をしていました。

void func()
{
  struct LocalFuncT {
     int operator()( int a ){
       return a + 100;
     }
  };
  LocalFuncT func_obj= LocalFuncT();

  func_obj( 23 );
}

ただし関数内で宣言された class ↑は template に渡すことができないなど
制限があります。(VisualC++ ではできました)

C++11 ではこの制限が撤廃されると同時に、Lambda 関数としてより簡単に
宣言して使えるようになっています。

template
struct CallbackObject {
  FT Func;
  CallbackObject( FT func ) : Func( func )
  {
  }
  int operator()( int a )
  {
    return  Func( a );
  }
};
template
CallbackObject CreateCallback( FT func )
{
  return  CallbackObject( func );
}

int s_func( int a )
{
  return a + 200;
}

struct FuncObject {
  int operator()( int a )
  {
     returtn a + 300;
  }
};

void func_test()
{
  CreateCallback( s_func )( 12 );  // pointer
  CreateCallback( FuncObject() )( 12 ); // object
  CreateCallback( []( int a ){ return a + 100; } )( 12 ); // lambda
}

今まで関数オブジェクトを扱っていた部分を Lambda 関数で置き換えられます。
オブジェクトなので引数の型を取り出そうとしてもそのままでは関数の型には
マッチしませんが、operator() は取れるようです。

template
struct CallbackObject {
  TT* This;
  FT  Func;
  CallbackObject( TT* t, FT func ) : This( t ), Func( func ) {}
  int operator()( int a )
  {
    return  (This->*Func)( a );
  }
};
template
CallbackObject CreateCallback( TT* t, FT func )
{
  return  CallbackObject( t, func );
}

struct func_class {
  void func_test()
  {
    auto func_obj2= [=]( int a ){ dump(this); return a + 400; };

    func_obj2( 12 );

    func_obj2.operator()( 12 );

    typedef decltype( func_obj2 )  FUNC_T2;
    CreateCallback( &func_obj2, &FUNC_T2::operator() )( 12 );
  }
};

特徴的なのはクロジャーとして変数を自動的に取り込めることで、
関数オブジェクトのメンバとして格納されています。
実際に vc/gcc でキャプチャなしの場合 struct {} 同様に sizeof() が 1 を返しており、
キャプチャ変数が存在する場合はキャプチャ分のサイズになります。

this はキャプチャ変数とみなされるため、自分自身へアクセスする方法は無いようです。
つまりキャプチャ変数に対する暗黙の “this->” が存在しており、
this も this->this 相当となっています。
キャプチャはコピー ([=]) または参照 ([&]) となり、
move ([&&]?) にはならないようです。

関連エントリ
C++11 Variadic Templates 可変長引数のテンプレート
スレッド同期命令の比較 C++11 とコンパイラ
C++11 Rvalue Reference 右辺値参照