C ++ 11ラムダ関数

この記事はhttps://www.cnblogs.com/WindSun/p/11182276.htmlから複製されています
。C++ 11は新しいラムダ関数を追加し、その基本的な形式は次のとおりです。

[捕捉列表] (参数) mutable -> 返回值类型 {函数体}

説明:

  • []はラムダのリーダーです。キャプチャリストは、ラムダ関数で使用するコンテキストで変数をキャプチャできます。
        [var]は、値の転送によって変数をキャプチャすることを意味します。var
        [=]は、値の転送がすべての親スコープ変数をキャプチャすることを意味します
        [&var]参照転送によって変数varをキャプチャすることを意味します
        [&]参照転送によってすべての親スコープ変数をキャプチャすることを意味します
        [this]値転送
       によって現在のこのポインタをキャプチャすることを意味しますいくつかの組み合わせもあります:
        [=、&a]はキャプチャすることを意味します、参照による値他の変数をキャプチャするためのメソッドの受け渡し
       注:
       キャプチャリストでは、[=、a]、[&、&this]などの変数を繰り返し送信することはできません。これにより、コンパイル中にエラーが発生します。
  • パラメータリストは、通常の関数のパラメータリストと一致しています。パラメータを渡す必要がない場合は、()[省略]でリンクできます。
  • Lambdaはデフォルトでconst属性であるため、mutableはLambdaのconstant属性をキャンセルできます。multableはLambda関数本体が値によって渡された変数を変更することのみを許可しますが、変更は外部変数に影響しません。
  • ->戻り値の型がvoidの場合は、接続-> [省略]できます。戻り値の型がクリアの場合は、省略してコンパイラに自動的に型を推測させることができます。
  • 関数本体は通常の関数と同じです。パラメーターに加えて、キャプチャされた変数を使用することもできます。

最も単純なLambda関数:

[]{
    
    }

使用例:

#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    
    
    int a = 5, b = 7;
    auto total = [](int x, int y)->int {
    
    return x + y; };    //接受两个参数
    cout << total(a, b) << endl;  //12
    
    auto fun1 = [=] {
    
    return a + b; };   //值传递捕捉父作用域变量
    cout << fun1() << endl; //12
    
    auto fun2 = [&](int c) {
    
    b = a + c; a = 1; };    //省略了返回值类型,引用捕获所有
    fun2(3);    
    cout << a <<" "<< b << endl; //1 8
    
    a = 5; b = 7;   //被修改后,重新赋值
    auto fun3 = [=, &b](int c) mutable {
    
    b = a + c; a = 1; };    //以值传递捕捉的变量,在函数体里如果要修改,要加mutaple,因为默认const修饰
    fun3(3);
    cout << a << " " << b << endl;    //5,8
    
    a = 5; b = 7;   //被修改后,重新赋值
    auto fun4 = [=](int x, int y) mutable->int {
    
    a += x; b += y; return a + b; };
    int t = fun4(10, 20);
    cout << t << endl;  //42
    cout << fun4(10, 20) << endl;  //72
    cout << a <<" "<< b << endl;    //5 7
    
    return 0;
}

ブロックスコープ外のLambda関数のキャプチャリストは空である必要があるため、このような関数は、構文の違いを除いて、通常の関数とそれほど変わりません。

ブロックのスコープ内のLambda関数は、ブロックのスコープ内の自動変数のみをキャプチャできます。このスコープ内にない非スコープまたは非自動変数(静的変数)は、コンパイラーにエラーを報告させます。

ラムダと静的インライン関数

Lambda関数は、外部で宣言された静的インライン関数を省略できます。これは、ローカル関数と同等です。ローカル関数は親スコープにのみ属します。外部の静的インライン関数やカスタムマクロと比較すると、Lambda関数は実行時にパフォーマンス上の利点はありませんが(悪くはありません)、Lambda関数の方が読みやすくなっています。
親関数が終了すると、Lambda関数は使用できなくなり、名前空間を汚染しなくなります。

値と可変によるキャプチャについて

前述のように、mutableはLambdaの定数属性をキャンセルできます。関数ドメインの値を変更する場合は、mutableを追加する必要があります。
最初の例:

#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    
    
    int j = 12;
    auto by_val = [=] {
    
    return j + 1; };
    auto by_ref = [&] {
    
    return j + 1; };
    cout << by_val() << endl;   //13
    cout << by_ref() << endl;   //13
    j++;
    cout << by_val() << endl;   //13
    cout << by_ref() << endl;   //14
    
    return 0;
}

上記の例では、j ++の後に値転送を呼び出した結果はまだ12です。理由は、値転送jは定数と見なされ、一度初期化されると、再度変更されることはありません(同じ定数と見なすことができます)。親スコープではjとして名前を付けます)、および参照キャプチャでは、jは引き続き親スコープの値です。

実際、値を渡すLambdaが関数に変換されると、クラスの定数メンバー関数になります。コードは基本的に次のとおりです。

class const_val_lambda
{
    
    
public:
    const_val_lambda(int v):val(v){
    
    }
public:
    void operator()()const {
    
     val = 3; } //报错
private:
    int val;
};

ただし、参照メソッドを使用してもエラーは報告されません。これは、参照自体は変更されず、参照の値のみが変更されるためです。

正確には、既存のC ++ 11標準のラムダに相当するものは、定数operatorOを持つファンクターです。したがって、キャプチャリストを使用するときは、バリューパスメソッドによってキャプチャされた変数がラムダ関数で変更できない定数であることに注意する必要があります。この標準の設計は、初期のSTLアルゴリズムのいくつかの設計上の欠陥に由来する可能性があります(ファンクターに制限がないため、特に適切に設計されていない一部のアルゴリズムでエラーが発生します)。より一般的に言えば、そのような設計は合理的であり、コンテキストからコピーされた一時変数を変更することは通常意味がありません。ほとんどの場合、一時変数はラムダ関数への入力としてのみ使用されます。結果をコンテキストに出力する必要がある場合は、参照を使用するか、ラムダ関数に値を返すことができます。さらに、ラムダ関数の可変修飾子はその一定性を排除できますが、これは実際には文法的な可能性を提供するだけです。実際には、可変ラムダ関数を使用する必要がある場所は多くないはずです。ほとんどの場合、デフォルトバージョン(変更不可)のラムダ関数を使用すれば十分です。

ラムダ関数と関数ポインター

Lambda関数は、単純な関数ポインター型やカスタム型ではありません。各Lambda関数は、クロージャ型の一時オブジェクト(右辺値)を生成します。ただし、C ++ 11では、次の条件を満たせば、Lambda関数を関数ポインターに変換できます。

  • Lambdaは変数をキャプチャしません
  • 関数ポインタによって示される関数プロトタイプは、Lambdaと同じ呼び出しメソッドを持っている必要があります
#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    
    
    int a = 3, b = 4;

    auto total = [](int x, int y)->int {
    
    return x + y; };
    typedef int(*all)(int x, int y);
    typedef int(*one)(int x);

    all p;
    p = total;
    //one q;
    //q = total;  //报错,参数不一致

    decltype(total) all_1 = total;
    //decltype(total) all_2 = p;  //报错,指针无法转换为Lambda

    return 0;
}

ラムダとSTL

C ++ 11以降、LambdaはforeachなどのSTLで広く使用されています。関数ポインタと比較して、関数ポインタには大きな欠陥があります。

  1. 関数は他の場所で定義されているため、読みにくいです。
  2. 関数ポインターを使用すると、コンパイラーがインライン最適化を実行しない可能性があります。ループの数が多すぎると、関数ポインターとラムダの間のパフォーマンスのギャップが大きくなります。関数2ポインターは、実行時に判別できる一部の状態では使用できません。C++ 11がない場合は、ファンクター関数のみを使用できます。これにより、STLアルゴリズムの学習コストが大幅に削減されます。
      しかし、ラムダはファンクターの完全な代替ではありません。Lambdaのキャプチャリストの制限により、副作用ドメインの変数のみをキャプチャできます。リリース機能は本質的にスコープ間で共有されます。

おすすめ

転載: blog.csdn.net/qq_24649627/article/details/110803657