1. ホーム
  2. c++

[解決済み】C++11のラムダ式って何?

2022-03-18 09:46:28

質問

C++11のラムダ式とは何ですか?どんな時に使うのでしょうか?ラムダ式は、導入前には不可能だったどのような問題を解決するのでしょうか?

いくつかの例とユースケースがあると便利です。

解決方法は?

問題点

C++には、以下のような便利な汎用関数があります。 std::for_eachstd::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++11 ではラムダが導入され、インラインの匿名ファンクタを書くことで struct f . 小さな単純な例では、この方が読みやすく(すべてを一箇所にまとめておける)、保守も簡単になる可能性があります。

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;
            }
        });
}

変数の捕捉

ここまでは、ラムダに渡されたもの以外をラムダ内で使っていなかったが、ラムダ内で、他の変数を使うこともできる。他の変数にアクセスしたい場合は、capture節( [] の式)は、これまでのところ、この例では使われていません、例えば。

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 にアクセスすると、デフォルトで これは、同じ入力で呼び出すと同じ結果になるという効果がありますが、次のようにすることができます。 としてラムダをマークします。 mutable を要求するために operator() を生成することはできません。 const .