C ++ 11のラムダ式とは何ですか?


1488

C ++ 11のラムダ式とは何ですか?いつ使用しますか?彼らが導入する前は不可能であった、どのような種類の問題を解決していますか?

いくつかの例と使用例が役立ちます。


14
ラムダが非常に便利なケースを見たことがあります。私の同僚は、数百万回の反復を含むコードを実行して、空間最適化の問題を解決していました。ラムダを使用すると、適切な関数よりもアルゴリズムがはるかに高速になりました。コンパイラは、Visual C ++ 2013である
sergiol

回答:


1491

問題

C ++には、std::for_eachやなどの便利な汎用関数が含まれておりstd::transform、非常に便利です。残念ながら、特に適用したいファンクタが特定の関数に固有である場合、それらを使用するのは非常に面倒な場合もあります。

#include <algorithm>
#include <vector>

namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}

void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

使うだけなら f一度し、その特定の場所で、クラス全体を記述して、些細なことを1つだけ行うのはやり過ぎです。

C ++ 03では、ファンクタをローカルに保つために、次のようなものを書きたくなるかもしれません。

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

ただし、これは許可されてfおらず、テンプレートに渡すことはできません、C ++ 03の関数に。

新しいソリューション

C ++ 11はラムダを導入し、インラインの匿名ファンクタを記述してを置き換えることができstruct fます。小さな単純な例の場合、これは読みやすく(すべてを1か所にまとめます)、たとえば最も単純な形式で維持するのが潜在的に簡単です。

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

ラムダ関数は、匿名ファンクタにとっては単なる構文上の砂糖です。

戻り型

単純なケースでは、ラムダの戻り値の型が推測されます。例:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

ただし、より複雑なラムダを書き始めると、戻り型がコンパイラーによって推定できない場合にすぐに遭遇します。例:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

これを解決するために、ラムダ関数の戻り値の型を明示的に指定できます-> T

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) -> double {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

変数の「取得」

これまでのところ、ラムダに渡されたもの以外は何も使用していませんが、ラムダ内で他の変数を使用することもできます。他の変数にアクセスしたい場合は[]、これらの例ではこれまで使用されていなかった(式の)キャプチャ句を使用できます。

void func5(std::vector<double>& v, const double& epsilon) {
    std::transform(v.begin(), v.end(), v.begin(),
        [epsilon](double d) -> double {
            if (d < epsilon) {
                return 0;
            } else {
                return d;
            }
        });
}

参照と値の両方でキャプチャできます。これは、&およびを使用して=それぞれ指定できます。

  • [&epsilon] 参照によるキャプチャ
  • [&] 参照によりラムダで使用されるすべての変数をキャプチャします
  • [=] ラムダで使用されるすべての変数を値でキャプチャします
  • [&, epsilon] [&]のような変数をキャプチャしますが、イプシロンを値で取得します
  • [=, &epsilon] [=]のような変数をキャプチャしますが、参照によりイプシロン

生成されたものoperator()constデフォルトであり、キャプチャーがconstデフォルトでそれらにアクセスしたときにキャプチャーが行われることを意味します。これは、同じ入力を使用する各呼び出しが同じ結果を生成するという効果がありますが、生成されるがでないことを要求するようmutableラムダをマークできます。operator()const


9
@Yakkあなたは閉じ込められました。キャプチャのないラムダには、関数型ポインタへの暗黙の変換があります。変換関数はconst常に...
Johannes Schaub-litb 2013年

2
@ JohannesSchaub-litbああ卑劣-そしてそれはあなたが呼び出すときに起こります()-それはゼロ引数のラムダとして渡されますが() const、ラムダと一致しないので、暗黙のキャストを含むそれを可能にする型変換を探します-to-function-pointer、それを呼び出す!卑劣!
Yakk-Adam Nevraumont 2013

2
興味深い-当初、ラムダはファンクタではなく匿名関数であると考えていたため、キャプチャのしくみについて混乱していました。
user253751 14年

50
プログラムで変数としてラムダを使用する場合は、次のように使用できます。 std::function<double(int, bool)> f = [](int a, bool b) -> double { ... }; ただし、通常は、コンパイラーにタイプを推定させます auto f = [](int a, bool b) -> double { ... }; (そして忘れないでください#include <functional>
Evert Heylen

11
return d < 0.00001 ? 0 : d;オペランドの1つが整数定数である場合にdoubleが返されることが保証されている理由を誰もが理解しているとは限りません(これは、?:演算子の暗黙の昇格規則が原因で、2番目と3番目のオペランドが通常の算術によって互いにバランスされています選択される変換に関係なく)。に変更する0.0 : dと、例が理解しやすくなる可能性があります。
ランディン

831

ラムダ関数とは何ですか?

C ++のラムダ関数の概念は、ラムダ計算と関数型プログラミングに由来しています。ラムダは名前のない関数であり、再利用が不可能で名前を付ける価値のない短いコードスニペットに対して(理論ではなく実際のプログラミングで)役立ちます。

C ++ではラムダ関数は次のように定義されています

[]() { } // barebone lambda

またはその栄光の中で

[]() mutable -> T { } // T is the return type, still lacking throw()

[]キャプチャリスト、()引数リスト{}、関数本体です。

キャプチャリスト

キャプチャリストは、ラムダの外側から何をどのように関数本体内で使用できるようにするかを定義します。次のいずれかになります。

  1. 値:[x]
  2. 参照[&x]
  3. 参照により現在スコープ内にある変数[&]
  4. 3と同じ、ただし値[=]

上記のいずれかをコンマ区切りのリストに混在させることができます[x, &y]

引数リスト

引数リストは、他のC ++関数と同じです。

関数本体

ラムダが実際に呼び出されたときに実行されるコード。

戻り型の控除

ラムダにreturnステートメントが1つしかない場合、戻り値の型は省略でき、暗黙の型はになりdecltype(return_statement)ます。

可変

ラムダが変更可能とマークされている場合(例 []() mutable { }:)、値によって取得された値をことができます。

ユースケース

ISO標準で定義されたライブラリは、ラムダから大きなメリットを得て、ユーザーがアクセス可能なスコープ内の小さなファンクターでコードを散らかす必要がないため、使いやすさが数バー向上します。

C ++ 14

C ++ 14では、ラムダはさまざまな提案によって拡張されています。

初期化されたラムダキャプチャ

キャプチャリストの要素をで初期化できるようになりました=。これにより、変数の名前を変更したり、移動してキャプチャしたりできます。標準からの例:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

とウィキペディアから取得したものでキャプチャする方法を示していますstd::move

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

ジェネリックラムダ

ラムダをジェネリックにすることができます(周囲のスコープのどこかに型テンプレート引数がある場合は、ここautoと同じになりTます T)。

auto lambda = [](auto x, auto y) {return x + y;};

戻り型の控除の改善

C ++ 14では、すべての関数で推定戻り値の型が許可されており、フォームの関数に限定されませんreturn expression;。これはラムダにも拡張されます。


2
上記の初期化されたラムダキャプチャの例で、なぜランバ関数を();で終了するのですか?これは[](){}();のように見えます。の代わりに [](){};。また、xの値は5にすべきではありませんか?
Ramakrishnan Kannan

7
@RamakrishnanKannan:1)()は、ラムダを定義した直後に呼び出して、yに戻り値を与えるためのものです。変数yは整数であり、ラムダではありません。2)いいえ、x = 5はラムダに対してローカルです(たまたま外側のスコープ変数xと同じ名前を持つ値によるキャプチャ)。その後、x + 2 = 5 + 2が返されます。外側の変数の再割り当ては、x、基準Rを介して行われますr = &x; r += 2;が、これは4の元の値に起こる
ヴィー

168

ラムダ式は通常、アルゴリズムをカプセル化して別の関数に渡せるようにするために使用されます。ただし、定義直後にラムダを実行することは可能です。

[&](){ ...your code... }(); // immediately executed lambda expression

機能的には

{ ...your code... } // simple code block

これにより、ラムダ式は複雑な関数をリファクタリングするための強力なツールになります。上記のように、コードセクションをラムダ関数でラップすることから始めます。明示的なパラメーター化のプロセスは、各ステップの後に中間テストを行って徐々に実行できます。コードブロックが完全にパラメーター化されたら(&)、コードを外部の場所に移動して、通常の機能にすることができます。

同様に、ラムダ式を使用して、アルゴリズムの結果に基づいて変数初期化できます...

int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!

プログラムロジックを分割する方法としてラムダ式を引数として別のラムダ式に渡すと便利な場合もあります...

[&]( std::function<void()> algorithm ) // wrapper section
   {
   ...your wrapper code...
   algorithm();
   ...your wrapper code...
   }
([&]() // algorithm section
   {
   ...your algorithm code...
   });

ラムダ式を使用すると、名前付きのネストされた関数を作成することもできます。これは、重複するロジックを回避する便利な方法です。名前付きラムダを使用すると、重要な関数をパラメーターとして別の関数に渡すときに、(匿名のインラインラムダと比べて)見た目が少し簡単になる傾向があります。 注:閉じ中括弧の後のセミコロンを忘れないでください。

auto algorithm = [&]( double x, double m, double b ) -> double
   {
   return m*x+b;
   };

int a=algorithm(1,2,3), b=algorithm(4,5,6);

後続のプロファイリングによって関数オブジェクトの初期化オーバーヘッドが大幅に明らかになった場合は、これを通常の関数として書き換えることを選択できます。


11
この質問が1.5年前に行われ、最後のアクティビティがほぼ1年前であることに気付きましたか?とにかく、あなたは私が今まで見たことのないいくつかの興味深いアイデアを提供しています!
Piotr99 2013年

7
定義と実行を同時に行うヒントをありがとう!私はそれの価値がためcontidionなどでその作品のことは注目に考えるif:文はif ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespace、仮定がiあるstd::string
ブラックライトシャイニング

74
したがって、以下は正しい表現です[](){}();
nobar 2013

8
うわっ!Pythonの(lambda: None)()構文は非常に読みやすくなっています。
dan04 2013年

9
@nobar-あなたは正しい、私はタイプミスした。これは合法です(今回はテストしました)main() {{{{((([](){{}}())));}}}}
Mark Lakata 2014年

38

答え

Q:C ++ 11のラムダ式とは何ですか?

A:内部的には、operator()constをオーバーロードした自動生成クラスのオブジェクトです。そのようなオブジェクトはクロージャーと呼ばれますとれ、コンパイラーによって作成されます。この「クロージャー」の概念は、C ++ 11のバインドの概念に近いものです。しかし、ラムダは通常、より優れたコードを生成します。また、クロージャーを介した呼び出しにより、完全なインライン化が可能になります。

Q:いつ使用しますか?

A:「シンプルで小さなロジック」を定義し、コンパイラに前の質問からの生成を実行するよう依頼します。operator()内に含めたいいくつかの式をコンパイラーに与えます。他のすべてのスタッフコンパイラが生成します。

Q:彼らが導入する前は不可能であった、どのような種類の問題を解決していますか?

A:これは、カスタムの追加、サブオペレーションの関数ではなく、演算子のオーバーロードのような構文糖のようなものですが、実際のロジックの1〜3行を一部のクラスにラップするなど、不要なコードの行をさらに節約します。一部のエンジニアは、行数が少ないとエラーが発生する可能性が低いと考えています(私もそう思います)

使用例

auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);

ラムダについての補足。質問には含まれません。興味がない場合は、このセクションを無視してください

1.キャプチャされた値。キャプチャできるもの

1.1。ラムダで静的ストレージ期間を持つ変数を参照できます。それらはすべて捕獲されます。

1.2。「値ごと」のキャプチャ値にラムダを使用できます。このような場合、キャプチャされた変数は関数オブジェクト(クロージャー)にコピーされます。

[captureVar1,captureVar2](int arg1){}

1.3。参考にキャプチャーできます。&-このコンテキストでは、ポインタではなく参照を意味します。

   [&captureVar1,&captureVar2](int arg1){}

1.4。すべての非静的変数を値または参照によってキャプチャする表記法が存在します

  [=](int arg1){} // capture all not-static vars by value

  [&](int arg1){} // capture all not-static vars by reference

1.5。すべての非静的変数を値によって、または参照によってキャプチャし、smthを指定する表記法が存在します。もっと。例:すべての非静的変数を値でキャプチャしますが、参照でキャプチャしますParam2

[=,&Param2](int arg1){} 

参照ではなくすべての静的でない変数をキャプチャしますが、値をキャプチャしますParam2

[&,Param2](int arg1){} 

2.戻り型の控除

2.1。ラムダが1つの式である場合は、ラムダの戻り値の型を推定できます。または、明示的に指定することもできます。

[=](int arg1)->trailing_return_type{return trailing_return_type();}

lambdaに複数の式がある場合、戻り値の型は末尾の戻り値の型を介して指定する必要があります。また、同様の構文を自動関数とメンバー関数に適用できます

3.キャプチャされた値。キャプチャできないもの

3.1。キャプチャできるのはローカル変数のみで、オブジェクトのメンバー変数はキャプチャできません。

4.Сonversions

4.1 !! ラムダは関数ポインターではなく、無名関数でもありませんが、キャプチャーなしのラムダは暗黙的に関数ポインターに変換できます。

PS

  1. ラムダ文法情報の詳細については、プログラミング言語の作業草案C ++#337、2012-01-16、5.1.2を参照してください。ラムダ式、p.88

  2. C ++ 14では、「init capture」という名前の追加機能が追加されました。クロージャーデータメンバーの任意の宣言を実行できます。

    auto toFloat = [](int value) { return float(value);};
    auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};

これ[&,=Param2](int arg1){}は有効な構文ではないようです。正しい形式は次のようになります[&,Param2](int arg1){}
GetFree

ありがとう。まず、このスニペットをコンパイルしてみました。キャプチャリストの許容修飾子で奇妙な非対称性のように見える// g ++ -std = c ++ 11 main.cpp -o test_bin; ./test_bin #include <stdio.h> int main(){#if 1 {int param = 0; auto f = [=、&param](int arg1)可変{param = arg1;}; f(111); printf( "%i \ n"、param); } #endif #if 0 {int param = 0; auto f = [&、= param](int arg1)可変{param = arg1;}; f(111); printf( "%i \ n"、param); } #endif return 0; }
bruziuz 2017

コメントでサポートされていない新しい行に見えます。次に、5.1.2ラムダ式、88ページ、「ワーキングドラフト、プログラミング言語C ++の標準」、文書番号:#337、2012-01-16を開きました。そして、文法構文を調べました。そして、あなたは正しいです。"= arg"によるキャプチャのようなものは存在しません
bruziuz

感謝します。説明を修正し、新しい知識を習得しました。
bruziuz 2017

16

ラムダ関数は、インラインで作成する無名関数です。一部のユーザーが説明したように変数をキャプチャできます(例:http : //www.stroustrup.com/C++11FAQ.html#lambda)が、いくつかの制限があります。たとえば、このようなコールバックインターフェイスがある場合、

void apply(void (*f)(int)) {
    f(10);
    f(20);
    f(30);
}

関数をその場で記述して、以下に適用するために渡された関数のように使用できます。

int col=0;
void output() {
    apply([](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

しかし、これを行うことはできません:

void output(int n) {
    int col=0;
    apply([&col,n](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

C ++ 11標準の制限のため。キャプチャを使用したい場合は、ライブラリと

#include <functional> 

(または間接的に取得するアルゴリズムのような他のSTLライブラリ)そして通常の関数を次のようにパラメータとして渡す代わりにstd :: functionを操作します:

#include <functional>
void apply(std::function<void(int)> f) {
    f(10);
    f(20);
    f(30);
}
void output(int width) {
    int col;
    apply([width,&col](int data) {
        cout << data << ((++col % width) ? ' ' : '\n');
    });
}

1
その理由は、ラムダはキャプチャがない場合にのみ関数ポインタに変換できるためです。applyファンクタを受け入れるテンプレートであれば、それは機能します
sp2danny

1
しかし、問題は、applyが既存のインターフェースである場合、プレーンな古い関数とは異なる方法で宣言できるという贅沢さがない可能性があることです。規格は、そのようなラムダ式が実行されるたびに、プレーンな古い関数の新しいインスタンスを生成できるように設計でき、キャプチャされた変数へのハードコードされた参照が生成されました。ラムダ関数がコンパイル時に生成されるようです。他の結果もあります。たとえば、静的変数を宣言すると、ラムダ式を再評価しても、新しい静的変数を取得できません。
Ted

1
多くの場合、関数ポインターは保存されることを意図しており、ラムダキャプチャは範囲外になる可能性があります。キャプチャレスラムダのみが関数ポインターに変換されるのは仕様による
sp2danny

1
どちらの方法でも、同じ理由で割り当て解除されるスタック変数に注意を払う必要があります。blogs.msdn.com/b/nativeconcurrency/archive/2012/01/29/を参照してください。 出力と適用を使用して記述した例は、代わりに関数ポインターが許可されて使用された場合にも機能するように記述されています。colは、applyからのすべての関数呼び出しが終了するまで割り当てられたままです。既存の適用インターフェースを使用して機能するように、このコードをどのように書き直しますか?グローバル変数または静的変数を使用することになるでしょうか、それとも、コードのさらにあいまいな変換を使用することになるでしょうか。
テッド

1
または、単にラムダ式が右辺値であり、したがって一時的なものであるということを単に意味しますが、コードは一定(シングルトン/静的)のままなので、後で呼び出すことができます。その場合、スタックに割り当てられたキャプチャが割り当てられている限り、関数は割り当てられたままになるはずです。もちろん、たとえば関数の多くのバリエーションがループ内に割り当てられている場合、それを巻き戻すのが面倒になる可能性があります。
2015年

12

lambda expressionC ++ Bjarne Stroustrupの著者による本の***The C++ Programming Language***第11章(ISBN-13:978-0321563842)で、最も優れた説明の1つが提供されています。

What is a lambda expression?

ラムダ式、時々も呼ばラムダ としての機能又は(厳密に言えば、誤ったが、口語) ラムダは、定義および使用するための簡略表記である匿名関数オブジェクト。operator()で名前付きクラスを定義し、後でそのクラスのオブジェクトを作成して、最後にそれを呼び出す代わりに、省略形を使用できます。

When would I use one?

これは、操作を引数としてアルゴリズムに渡したい場合に特に便利です。グラフィカルユーザーインターフェイス(およびその他の場所)のコンテキストでは、そのような操作はコールバックと呼ばれることがよくあります

What class of problem do they solve that wasn't possible prior to their introduction?

ここで私はラムダ式で行われたすべてのアクションはそれらなしで解決できると思いますが、はるかに多くのコードとはるかに大きな複雑さがあります。ラムダ式これは、コードを最適化する方法であり、コードをより魅力的にする方法です。Stroustupによる悲しみ:

最適化の効果的な方法

Some examples

ラムダ式を介して

void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
    for_each(begin(v),end(v),
        [&os,m](int x) { 
           if (x%m==0) os << x << '\n';
         });
}

または関数経由

class Modulo_print {
         ostream& os; // members to hold the capture list int m;
     public:
         Modulo_print(ostream& s, int mm) :os(s), m(mm) {} 
         void operator()(int x) const
           { 
             if (x%m==0) os << x << '\n'; 
           }
};

あるいは

void print_modulo(const vector<int>& v, ostream& os, int m) 
     // output v[i] to os if v[i]%m==0
{
    class Modulo_print {
        ostream& os; // members to hold the capture list
        int m; 
        public:
           Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
           void operator()(int x) const
           { 
               if (x%m==0) os << x << '\n';
           }
     };
     for_each(begin(v),end(v),Modulo_print{os,m}); 
}

あなたが必要な場合はlambda expression、以下のような名前を付けることができます:

void print_modulo(const vector<int>& v, ostream& os, int m)
    // output v[i] to os if v[i]%m==0
{
      auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
      for_each(begin(v),end(v),Modulo_print);
 }

または別の簡単なサンプルを想定する

void TestFunctions::simpleLambda() {
    bool sensitive = true;
    std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});

    sort(v.begin(),v.end(),
         [sensitive](int x, int y) {
             printf("\n%i\n",  x < y);
             return sensitive ? x < y : abs(x) < abs(y);
         });


    printf("sorted");
    for_each(v.begin(), v.end(),
             [](int x) {
                 printf("x - %i;", x);
             }
             );
}

次に生成されます

0

1

0

1

0

1

0

1

0

1

0ソート済みx-1; x-3; x-4; x-5; x-6; x-7; x-33;

[]-これはキャプチャリストまたはlambda introducerlambdasローカル環境へのアクセスが必要ない場合は、使用できます。

本からの引用:

ラムダ式の最初の文字は常に[です。ラムダイントロデューサは、さまざまな形式をとることができます。

[]:空のキャプチャリスト。これは、周囲のコンテキストのローカル名をラムダ本体で使用できないことを意味します。このようなラムダ式の場合、データは引数または非ローカル変数から取得されます。

[&]:参照により暗黙的にキャプチャします。すべてのローカル名を使用できます。すべてのローカル変数は参照によってアクセスされます。

[=]:暗黙的に値でキャプチャします。すべてのローカル名を使用できます。すべての名前は、ラムダ式の呼び出し時に取得されたローカル変数のコピーを指します。

[capture-list]: 明示的なキャプチャ。キャプチャリストは、参照または値によってキャプチャ(つまり、オブジェクトに格納)されるローカル変数の名前のリストです。&が前に付いた名前の変数は、参照によってキャプチャされます。他の変数は値によってキャプチャされます。キャプチャリストには、これと名前の後に...を要素として含めることもできます。

[&、キャプチャリスト]リストに記載されていない名前を持つすべてのローカル変数を暗黙的に参照してキャプチャします。キャプチャリストにはこれを含めることができます。リストされた名前の前に&を付けることはできません。キャプチャリストで指定された変数は、値でキャプチャされます。

[=、キャプチャリスト]:リストに記載されていない名前を持つすべてのローカル変数を暗黙的に値でキャプチャします。キャプチャリストにこれを含めることはできません。リストされた名前の前には&を付ける必要があります。キャプチャリストで名前が付けられた変数は、参照によってキャプチャされます。

&が前に付いているローカル名は常に参照によってキャプチャされ、&が前に付いていないローカル名は常に値によってキャプチャされることに注意してください。参照によるキャプチャのみが、呼び出し環境での変数の変更を許可します。

Additional

Lambda expression フォーマット

ここに画像の説明を入力してください

追加の参照:


いい説明。範囲ベースのforループを使用すると、ラムダを回避してコードを短縮できますfor (int x : v) { if (x % m == 0) os << x << '\n';}
ディートリッヒバウムガルテン

2

まあ、私が見つけた1つの実用的な使用法は、ボイラープレートコードの削減です。例えば:

void process_z_vec(vector<int>& vec)
{
  auto print_2d = [](const vector<int>& board, int bsize)
  {
    for(int i = 0; i<bsize; i++)
    {
      for(int j=0; j<bsize; j++)
      {
        cout << board[bsize*i+j] << " ";
      }
      cout << "\n";
    }
  };
  // Do sth with the vec.
  print_2d(vec,x_size);
  // Do sth else with the vec.
  print_2d(vec,y_size);
  //... 
}

ラムダなしでは、さまざまなbsizeケースで何かをする必要があるかもしれません。もちろん関数を作成することもできますが、soul user関数のスコープ内で使用を制限したい場合はどうでしょうか?ラムダの性質はこの要件を満たし、私はその場合に使用します。


2

C ++のラムダは、「すぐに使える関数」として扱われます。はい、文字通り外出先で、あなたはそれを定義します。これを使って; 親関数スコープが終了すると、ラムダ関数はなくなります。

c ++はc ++ 11でそれを導入し、誰もがあらゆる可能な場所で同じように使い始めました。例とラムダとは何かはここで見つけることができますhttps://en.cppreference.com/w/cpp/language/lambda

存在しないがすべてのC ++プログラマーが知っておくべき重要な点について説明します

ラムダはあらゆる場所で使用することを意図したものではなく、すべての関数をラムダで置き換えることはできません。また、通常の機能と比較して最速ではありません。ラムダで処理する必要があるオーバーヘッドがあるためです。

それは確かにいくつかのケースで行数を減らすのに役立ちます。基本的には、同じ関数で1回以上呼び出されるコードのセクションに使用できます。そのコードは、スタンドアロン関数を作成できるようにするために、他の場所には必要ありません。

以下はラムダの基本的な例とバックグラウンドで何が起こるかです。

ユーザーコード:

int main()
{
  // Lambda & auto
  int member=10;
  auto endGame = [=](int a, int b){ return a+b+member;};

  endGame(4,5);

  return 0;

}

コンパイルがそれを拡張する方法:

int main()
{
  int member = 10;

  class __lambda_6_18
  {
    int member;
    public: 
    inline /*constexpr */ int operator()(int a, int b) const
    {
      return a + b + member;
    }

    public: __lambda_6_18(int _member)
    : member{_member}
    {}

  };

  __lambda_6_18 endGame = __lambda_6_18{member};
  endGame.operator()(4, 5);

  return 0;
}

ご覧のとおり、使用するとどのようなオーバーヘッドが追加されますか。したがって、どこでも使用することはお勧めできません。該当する場所で使用できます。


はい、文字通り外出先で、あなたはそれを定義します。これを使って; 親関数のスコープが終了すると、ラムダ関数はなくなります。関数が呼び出し側にラムダを返すとどうなりますか?
Nawaz

1
また、通常の機能に比べて最速ではありません。ラムダで処理する必要があるオーバーヘッドがあるためです。あなたが今まで持って実際にベンチマークを実行この主張をサポートするために?逆に、lambda +テンプレートは、可能な限り最速のコードを生成することがよくあります。
Nawaz

1

解決する1つの問題:constメンバーを初期化するために出力パラメーター関数を使用するコンストラクターでの呼び出しのラムダよりも単純なコード

クラスのconstメンバーを初期化するには、その出力を出力パラメーターとして返すことで値を設定する関数を呼び出します。


これは単純な関数で行うこともできます。これは、リンク先の質問に対する受け入れられた回答が行うように言っていることでもあります。
SirGuy 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.