ラムダ式とは何ですか?いつ使用すればよいですか?質問する

ラムダ式とは何ですか?いつ使用すればよいですか?質問する

C++11 のラムダ式とは何ですか? いつ使用しますか? ラムダ式は、導入前には解決できなかったどのような種類の問題を解決しますか?

いくつかの例と使用例が役立つでしょう。

ベストアンサー1

問題

std::for_eachC++には、や のような便利な汎用関数が含まれています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、些細なことや一度きりのことを行うためだけにクラス全体を書くのはやりすぎのように思えます。

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, zeta]イプシロンを参照によって、ゼータを値によって取得します
  • [&]ラムダで使用されるすべての変数を参照によってキャプチャします
  • [=]ラムダで使用されるすべての変数を値で取得します
  • [&, epsilon]ラムダで使用されるすべての変数を参照でキャプチャしますが、イプシロンは値でキャプチャします。
  • [=, &epsilon]ラムダで使用されるすべての変数を値でキャプチャしますが、イプシロンは参照でキャプチャします。

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

おすすめ記事