C++面接C++11新特性のlambda

7444 ワード

Lambdaの概要
Pythonに詳しいプログラマーはlambdaに慣れていないはずだ.簡単に言えば、lambdaは匿名の呼び出し可能なコードブロックです.C++11の新しい規格では、lambdaは以下のフォーマットを有する.
[capture list] (parameter list) -> return type { function body }

4つの構成要素があります
  • capture list:取得リスト
  • parameter list:パラメータリスト
  • return type:戻りタイプ
  • function body:実行コードでは、パラメータリストと戻りタイプは無視できます.

  • 次に、いくつかの簡単な例を具体的に見ます.
    auto f1 = [] { return 1; };
    auto f2 = [] () { return 2; };
    cout<'\t'<

    取得リスト
    Lambdaのキャプチャリストは、値も参照もキャプチャできます.
    取得値:
    int test_data[] = {1, 5, 9, 7, 3, 19, 13, 17};
    int border = 8;
    auto f3 = [border](const int &i){ if(i > border) cout<<i<t'; };
    for_each(begin(test_data), end(test_data), f3);
    cout<<endl;

    リファレンスの取得:
    auto f4 = [&border](const int &i){ if(i > border) cout<<i<t'; };
    border = 6;
    for_each(begin(test_data), end(test_data), f4);
    cout<<endl;

    出力からlambdaで機能するborderは修正後の6であり,キャプチャが参照であることが確認された.
    参照をキャプチャする場合、lambdaが呼び出されたときも、この参照が有効であることを保証する必要があることに注意してください.
    キャプチャリストは、コンパイラにlambdaの実行コードを介して、どのローカル変数をキャプチャする必要があるかを判断させる暗黙的なキャプチャ方式も採用することができる.
    暗黙的なキャプチャでは、値、参照、または両方をキャプチャできます.
    char space = ' ';
    auto f5 = [=](const int &i){ if(i > border) cout<'\t'; };
    auto f6 = [&](const int &i){ if(i > border) cout<'\t'; };
    auto f7 = [&, space](const int &i){ if(i > border) cout<0;
    for_each(begin(test_data), end(test_data), f5);
    cout<cout<cout<

    ここでのf 7で使用されるブレンド形式は、「spaceキャプチャ値以外の変数はリファレンスをキャプチャする」と読むことができる.
    可変lambda
    Lambdaが値によって取得された変数の値を変更する必要がある場合は、lambdaにmutableキーワードを付ける必要があります.コンパイルエラーが発生します.
    auto f8 = [&, space](const int &i) mutable { if(i > border) 
    {cout<<i<<space; space='\t';} };
    for_each(begin(test_data), end(test_data), f8);
    cout<<endl;
    cout<<1<<space<<2<<endl;

    出力から、lambda f 8におけるspaceの値は、最初の呼び出し後、タブTabに変更されることがわかる.しかしlambdaの外ではspaceは依然としてスペースである.
    戻りタイプ
    Lambdaの戻りタイプは、テール戻りタイプを採用します.一般的:
  • lambda return文のみが含まれている場合、コンパイラはその戻りタイプを推定することができ、指定した戻りタイプを表示しなくてもよい.
  • それ以外の場合、コンパイラはlambdaがvoidを返すと仮定し、voidを返す関数は特定の値を後悔することはできません.これは多くの場合矛盾しているため、指定した戻りタイプを表示する必要があります.しかし、実際のテストにより、現在のg++コンパイラはよりスマートになりました.第2点では、コンパイラがlambda関数体から関数の戻りタイプを推定できる限り、戻りタイプを明示的に指定する必要はありません.例えば、
  • auto f9 = [](const int i){if(i % 3) return i * 3; else return i;};
    transform(begin(test_data), end(test_data), begin(test_data), f9);
    border = 0;
    for_each(begin(test_data), end(test_data), f6);
    cout<

    Lambdaコードブロックには複数のreturn文があり、if/else文もありますが、コンパイラはreturn文から戻り値がintタイプであるべきだと推定できるので、末尾戻りタイプを省略できます.
    ただし、次のような形式では、コンパイラは戻りタイプを推定する際に一致しないことを発見したため、戻りタイプを明示的に指定する必要があります.
    auto f10 = [](const int i) -> double
    {if(i % 5) return i * 5.0; else return i;};
    transform(begin(test_data), end(test_data), begin(test_data), f10);
    for_each(begin(test_data), end(test_data), f6);
    cout<

    まとめ
  • lambda式形式:[capture list](parameter list)->return type{function body}で、parameter listとreturn typeは省略できます.
  • キャプチャリストは、値[val]をキャプチャしてもよいし、参照[&ref]をキャプチャしてもよい.
  • キャプチャリストは、ローカル変数を暗黙的にキャプチャすることもでき、同様にキャプチャ値[=]とキャプチャ参照[&]の2つの方法があり、初回以外にもキャプチャ[&,val]または[=,&ref]を混合することもできる.
  • lambdaが取得した値を変更する必要がある場合、mutableキーワードを追加する必要があります.
  • lambdaが戻り値タイプを自動的に推定できない場合は、戻りタイプを末尾に表示する方法で指定を表示する必要があります.