2012年1月14日土曜日

lambda式

lambda(ラムダ)式とは

式とステートメントを含めた匿名関数オブジェクトを生成するための式のことです。

あるロジックを特定の関数などに渡したい場合は、関数オブジェクトを生成して渡したり関数ポインタを使用したりすることができます。しかし、関数オブジェクトや関数ポインタを使用するためにはクラスや関数をあらかじめ定義する必要があり、簡単にロジックだけを渡すことができません。そこでlambda式を使用することで、記述がシンプルになります。具体的な使い方について説明します。

lambda式を定義する

lambda式は次のように定義します。

// [capture] mutable (引数) -> 戻り値型 { 式 };
auto add = [](int a, int b) -> int { return a + b; };
std::cout << add(1, 2) << std::endl;

captureについては後述します。

引数は、通常の関数と同じように引数を指定できます。ただし、デフォルト値の指定、可変長引数、名前のない引数は指定できません。

戻り型も通常の関数と同じように指定できますが、引数の後ろに置く記述方法になります。戻り値がない場合や式の内容がreturn文1つだけの場合は省略することができます。

式には、通常のC++の式を記述することができます。

lambda式を引数に取る

lambda式を関数の引数に取るには、次のようにテンプレートを使用します。

template <typename PRINT>
void func(PRINT print) {
  print();
}

int main() {
  auto add = [](int a, int b) -> int { return a + b; };
  std::cout << add(1, 2) << std::endl;
  func([add](){ std::cout << add(1, 2) << std::endl;});
}

上記の例で示したとおり、captureに指定することでlambda式をlambda式内で使用することも可能です。

capture

lambda式を引数に取る例でも触れたように、特定のオブジェクト(変数など)をcapture(キャプチャ)として指定することでlambda式内で使用する事が可能になります。

captureで指定した変数は、lambda式内で変更することができません。そのような式を書くとコンパイルエラーになります。ただし、captureで指定するときに"&"をつけて指定すると、オブジェクトの参照としてlambda式内で使用することができ、値を変更することができます。"&"をつけた変数を参照キャプチャと呼び、"&"がついていない変数を値キャプチャと呼びます。

  int a = 0, b = 0;
  auto p = [a, &b](){
    std::cout << "a=" << a << std::endl;
    std::cout << "b=" << b++ << std::endl;
  };
  p(); // a=0, b=0
  std::cout << "a=" << a << std::endl; // a=0
  std::cout << "b=" << b << std::endl; // b=1
  a = 10;
  b = 20;
  p(); // a=0, b=20

上記の例のとおり、値キャプチャはlambda式の外で値を変更しても、それがlambda式に影響することはありません。参照キャプチャはlambda式の外の処理がlambda式に影響しており、更にその逆のlambda式の変更がlambda式の外にも影響しています。

captureに"&"のみを指定した場合は、すべての外部変数を参照キャプチャとして参照することができます。また、"="のみを指定した場合は、すべての外部変数を値キャプチャとして参照することができます。

  [&](){}; // すべての外部変数を参照キャプチャとして参照する。
  [=](){}; // すべての外部変数を値キャプチャとして参照する。
  [&, a](){}; // 変数aは値キャプチャ、それ以外の外部変数を参照キャプチャとして参照する。
  [=, &a](){}; // 変数aは参照キャプチャ、それ以外の外部変数を値キャプチャとして参照する。

TODO

lambdaの構文に、mutable,throwを指定できるか確認する。

0 件のコメント:

コメントを投稿