C++新特性~lambda抜粋

33521 ワード

Lambda関数と表現[編集]
C++11標準は匿名関数のサポートを提供し、『ISO/IEC 14882:2011』(C++11標準ドキュメント)ではlambda式と呼ばれている[10].lambda式には以下の形式がある.
[capture] (parameters) mutable exception attribute -> return_type { body }

四角カッコで囲まれたcaptureリストでlambda式の定義を開始する必要があります.
Lambda関数のパラメータテーブルは、通常の関数のパラメータテーブルよりも3つの制限があります.
  • パラメータにデフォルト値があることはできません
  • 可変長パラメータリスト
  • を持つことはできない.
  • 無名パラメータ
  • は使用できません.
    lambda関数にパラメータがなく、mutable、exception、またはattribute宣言がない場合、パラメータの空のカッコは省略できます.ただし、mutable、exception、またはattribute宣言を指定する必要がある場合は、パラメータが空であってもカッコは省略できません.
    関数体にreturn文が1つしかない場合、または戻り値タイプがvoidの場合、戻り値タイプ宣言は省略できます.
    [capture](parameters){body}
    

    lambda関数の例は次のとおりです.
    [](int x, int y) { return x + y; } //  return             
    [](int& x) { ++x; }   //   return   -> lambda       void
    []() { ++global_x; }  //     ,           
    []{ ++global_x; }     //      ,()     
    

    この無名関数の戻り値はdecltype(x+y)(上記の第1の例では)である.lambda関数体の形式がreturn expressionである場合、あるいは何でも返すたびに、あるいはすべての戻り文がdecltypeで同じタイプを検出できる場合、戻り値タイプを省略することができる.
    特に、sortおよびfindのようなC++標準ライブラリアルゴリズム関数を使用する場合、使用者は、アルゴリズム関数呼び出しの近傍で一時的な部分関数(述語関数、predicate functionとも呼ばれる)を定義することをしばしば望んでいる.言語自体が関数内部でクラスを定義できるため、関数オブジェクトの使用を考慮することができますが、これは通常、面倒で冗長であり、コードの流れを阻害します.また,標準C++では関数内部に定義されたクラスをテンプレートに使用することは許されないので,前述のやり方は不可能である.
    C++11のlambdaへのサポートは上記の問題を解決することができる.
    lambda関数は、次のように定義できます.
    [](int x, int y) { return x + y; }
    

    この無名関数の戻りタイプはdecltype(x+y)である.lambda関数が「return expression」の形式に合致する場合にのみ、その戻りタイプは無視されます.前述の場合、lambda関数は1つの文のみとすることができる.
    より複雑な例では、戻りタイプは以下のように明確に指定できます(戻り値タイプは明示的に指定できます).
    [](int x, int y) -> int { int z = x + y; return z + x; }
    

    この例では、中間結果を格納するために一時的なパラメータzが作成される.一般的な関数と同様に、zの値は、次回この名前のない関数が再び呼び出されるまで保持されません(中間値は呼び出しの前後には存在しません).
    lambda関数がvoidなどの戻り値を返さない場合、その戻りタイプは完全に無視され得る(戻り値を明示的に指定する必要はなく、-> voidコードを書く必要はない).
    lambda関数と同じ役割ドメインを定義するパラメータ参照も使用できます.このようなパラメータセットは一般にclosure(閉パケット)と呼ばれる.
    Lambda関数はlambda関数以外のautomatic storage durationを有する変数をキャプチャすることができる.関数体をこれらの変数の集合と合わせて閉パケットと呼ぶ.これらの外部変数は、lambda式を宣言するときに、四角カッコ[および]に列挙される.空の四角カッコは、外部変数がcaptureされていないことを示します.これらの変数は、値伝達によって取得または参照によって取得されます.値が取得された変数の場合、デフォルトは読み取り専用です.これらの変数を変更すると、コンパイルエラーが発生します.しかし、lambda式のパラメータテーブルのカッコの後ろにmutableキーワードを使用すると、lambda関数内の文が伝値参照の変数を変更することができます.これらの変更はlambda式と(実際には関数オブジェクトで実装されています)同じライフサイクルがありますが、伝達値によって取得された外部変数の値には影響しません.lambda関数はstaticストレージ期間を持つ変数を直接使用できます.lambda関数の取得リストにstaticストレージ期間の変数が与えられている場合、コンパイル時に警告が表示され、lambda関数に従って直接これらの外部変数を使用して処理されます.このstaticストレージ期間を持つ変数は、トランスポートキャプチャとして宣言されても、変更は実際に外部変数を直接変更します.コンパイラはlambda関数に対応する関数オブジェクトを生成する場合、関数オブジェクトのデータメンバーで「キャプチャ」されたstaticストレージ期間の変数を保持しません.例:
    []  //         。            。
    [x, &y] // x        (  ),y         。
    [&]   //                         。
    [=]   //                         。
    [&, x]   // x             。             。
    [=, &z]   // z             。             。
    

    closureは次のように定義され、使用されます.
    std::vector<int> someList;
    int total = 0;
    std::for_each(someList.begin(), someList.end(), [&total](int x) {
      total += x;
    });
    std::cout << total;
    

    上記の例では、someList要素の合計を計算し、印刷することができます.パラメータtotalはlambda関数closureの一部であり、述語関数に参照で渡されるため、その値はlambda関数によって変更することができる.
    参照されたシンボル&を使用しない場合、代表パラメータはlambda関数に値を伝達するように入力されます.使用者は,この表現法を用いてパラメータ伝達の方法,すなわち伝達値,または伝達参照を明確に区別できる.lambda関数は、std::functionオブジェクトに配置するなど、宣言された場所でその場で使用できないため、この場合,パラメータがclosureに参照でリンクされている場合は,無意味で危険な行為である.
    lambda関数が定義された役割ドメインでのみ使用される場合は、stackに参照されているすべてのパラメータを表すlambda関数を[&]で宣言できます.すべてのパラメータは、参照によって入力されます.明示的に指定する必要はありません.
    std::vector<int> someList;
    int total = 0;
    std::for_each(someList.begin(), someList.end(), [&](int x) {
      total += x;
    });
    

    パラメータがlambda関数に伝達される方法は、実装によって変化する可能性があり、一般に、lambda関数がその役割ドメイン関数のstackポインタを保持し、これによって領域パラメータにアクセスすることが望ましい.
    [&]ではなく[=]を使用すると、すべての参照を表すパラメータが値伝達で使用されます.
    異なるパラメータの場合、値または参照は混和して使用できます.例えば、使用者は、すべてのパラメータを参照で使用することができますが、1つの値で使用されるパラメータがあります.
    int total = 0;
    int value = 5;
    [&, value](int x) { total += (x * value); };
    

    totalはlambda関数に参照を伝達する方式で伝達され、valueは伝達値である.
    クラスの非静的メンバー関数で定義されたlambda式は、thisポインタを明示的または暗黙的にスナップすることができ、クラスオブジェクトのデータメンバーおよび関数メンバーを参照することができる.Lambda関数の関数体では、次の変数にアクセスできます.
  • 関数パラメータ
  • ローカル宣言変数
  • クラスデータメンバー(関数がクラスに宣言された場合)
  • 静的記憶期間を有する変数(例えばグローバル変数)
  • 取得外部変数
  • 明示的に取得された変数
  • 暗黙的に取得された変数.デフォルトの取得モード(値または参照)を使用してアクセスします.

  • Lambda関数のデータ型は関数オブジェクトであり、保存時にstd::functionテンプレートタイプまたはautoキーワード、またはautoキーワードを使用する必要があります.例:
    #include 
    #include 
    #include 
     
    double eval(std::function <double(double)> f, double x = 2.0)
    {
    	return f(x);
    }
     
    int main()
    {
    	std::function<double(double)> f0    = [](double x){return 1;};
    	auto                          f1    = [](double x){return x;};
    	decltype(f0)                  fa[3] = {f0,f1,[](double x){return x*x;}};
    	std::vector<decltype(f0)>     fv    = {f0,f1};
    	fv.push_back                  ([](double x){return x*x;});
    	for(int i=0;i<fv.size();i++)
    		std::cout << fv[i](2.0) << std::endl;
    	for(int i=0;i<3;i++)
    		std::cout << fa[i](2.0) << std::endl;
    	for(auto &f : fv)
    		std::cout << f(2.0) << std::endl;
    	for(auto &f : fa)
    		std::cout << f(2.0) << std::endl;
    	std::cout << eval(f0) << std::endl;
    	std::cout << eval(f1) << std::endl;
    	std::cout << eval([](double x){return x*x;}) << std::endl;
    	return 0;
    }
    

    lambda関数のスナップ式が空の場合、通常の関数ポインタで格納または呼び出すことができます.例:
    auto a_lambda_func = [](int x) { /*...*/ };
    void (* func_ptr)(int) = a_lambda_func;
    func_ptr(4); //calls the lambda.

    lambda関数がクラスのメンバー関数に定義されている場合、クラスオブジェクトの参照を使用して、内部のメンバーにアクセスできます.
    [](SomeType *typePtr) { typePtr->SomePrivateMemberFunction(); };
    

    これはlambda関数が作成した役割ドメインがSomeTypeのメンバー関数の内部にある場合にのみ動作します.
    メンバー関数でオブジェクトを指すthisポインタは、lambda関数に明示的に入力する必要があります.そうしないと、メンバー関数のlambda関数では、オブジェクトのパラメータまたは関数は使用できません.
    [this]() { this->SomePrivateMemberFunction(); };
    

    lambda関数が[&]または[=]の形式を使用する場合、thisはlambda関数で表示されます.
    Lambda関数はコンパイラ依存型の関数オブジェクトです.このタイプの名前はコンパイラ自身でしか使用できません.使用者がlambda関数をパラメータとして入力する場合は、そのタイプがテンプレートタイプであるか、std::functionを作成してlambdaの値を取得する必要があります.autoキーワードを使用すると、lambda関数を格納できます.
    auto myLambdaFunc = [this]() { this->SomePrivateMemberFunction(); };
    auto myOnheapLambdaFunc = new auto([=] { /*...*/ });