概要

ラムダ式(無名関数(anonymous function, nameless function)、匿名関数)とは、関数を定義するための記法、文法のことである。

C++では、ラムダ式はC++11からサポートされた。g++コンパイラを使用する場合は、以下のようにC++11を有効にする必要がある。
また、C++11とC++14で仕様が異なる。C++14版は、より高度になっている。

g++49 -std=c++11 lambda.cpp
c++ -std=c++11 lambda.cpp


C++のラムダ式以下の特徴がある。

  • 関数オブジェクトであり、生成されたクラスのoperator()になる。
  • std::functionに代入できる。
  • STLの引数に使用できる。また、コールバック関数等の引数としてラムダ式を直接記述できる。
  • ローカル変数をキャプチャできる。
  • 引数や戻り値の型推論(auto)も利用できる。



ラムダ式の文法

 int main(int argc, char *argv[])
 {
    []  // ラムダキャプチャー
    ()  // パラメータ定義節
    {}  // 複合ステートメント
    ()  // 関数呼び出し式
    ;
 
    return 0;
 }



最小のラムダ式の定義

 int main(int argc, char *argv[])
 {
    auto func = [](){};
 
    return 0;
 }


引数は省略可能であるため、さらに省略できる。

 int main(int argc, char *argv[])
 {
    auto func = []{};
 
    return 0;
 }



最小のラムダ式の呼び出し

ラムダ式を呼び出すには、関数呼び出し式()を使用する。

 int main(int argc, char *argv[])
 {
    [](){}();
    return 0;
 }



サンプルコード

ラムダ式で書くHello World
 #include <iostream>
 
 int main(int argc, char const* argv[])
 {
    []{ std::cout << "Hello World" << std::endl;}();
 
    return 0;
 }


ラムダ式を変数に代入する

ラムダ式を変数に代入することができる。関数ポインタのような感覚で呼び出せる。

 #include <iostream>
 
 int main(int argc, char const* argv[])
 {
    auto func = []{std::cout << "Hello world" << std::endl;};
    func();  //ラムダ式の呼び出し
 
    return 0;
 }


ラムダ式を関数に渡す

ラムダ式を別の関数に渡すことができる。(コールバック関数)

 #include <iostream>
 
 template<typename Func>
 void f(Func func)
 {
    func();
 }
 
 int main(int argc, const char *argv[])
 {
    f([]{std::cout << "Hello world" << std::endl;});
 
    return 0;
 }


ラムダ式に引数を与える
 #include <iostream>
 #include <string>
 
 int main(int argc, const char *argv[])
 {
    [](const string &str) // 引数の定義
    {                     // 関数本体
       std::cout << str << std:endl;
    }
    ("I am Argument!");   // 関数呼び出しと引数
 
    return 0;
 }


ラムダ式で戻り値を返す

ラムダ式は、戻り値を返すことができる。
1つ目は、明示的に戻り値の型を定義していない。
2つ目は、明示的に戻り値の型を定義している。アロー演算子の記法([]()->型)で表現している。

 #include <iostream>
 
 int main(int argc, const char *argv[])
 {
    auto a = []{return 0;};                 // 戻り値を推測させる
    auto b = []() -> float {return 3.14;};  // 戻り値を明示的に定義する
 
    std::cout << a << std::endl;
    std::cout << b << std::endl;
 
    return 0;
 }


変数のキャプチャ

ラムダ式は、式が定義される関数のスコープの変数をキャプチャできる。
JavaScriptでいうクロージャが、スコープにある変数を参照できることをイメージすること。

 #include <iostream>
 #include <string>

 int main(int argc, char const* argv[])
 {
    string x = "I am string";
    [&]{std::cout << x << std::endl;}();  // 参照
    [=]{std::cout << x << std::endl;}();  // コピー
 
    return 0;
 }


参照の場合の代入

参照での代入とコピーでの代入を行う。

 #include <iostream>
 #include <string>
 
 int main(int argc, const char *argv[])
 {
    string s1 = "I am s1";
    [&]{s1 = "Overwrite s1!";}();
 
    //string s2 = "I am s2";
    //[=]{s1 = "Overwrite s2!";}();   C++11ではコンパイルエラーが起きる
 
    std::cout << s1 << std::endl;
    //std::cout << s2 << std::endl;
 
    return 0;
 }


mutable

コピーでの代入では、変数を書き変えることができないが、mutableを使用することで、ラムダ式の中だけ変数を書き変えることができる。
しかし、コピーのため、元の変数の値は変更できない。
mutableを指定した時、ラムダ式の引数リストの括弧は省略できない。

 #include <iostream>
 #include <string>
 
 int main(int argc, const char *argv[])
 {
    std::string s1 = "I am s1";
    [=]() mutable {s1 = "Overwrite s1!"; std::cout << s1 << std::endl;}();
 
    std::cout << s1 << std::endl;
 
    return 0;
 }


また、mutableを指定した時、明示的に戻り値を指定する場合は、以下のようにする。

 auto dRet = []() mutable -> double {return 3.14;};


キャプチャの指示

ラムダキャプチャ[]には、変数ごとにキャプチャの方法(コピーまたは参照)を指定できる。

 #include <iostream>

 int main(int argc, const char *argv[])
 {
    int a = 0,
        b = 0;
 
    [a, &b]() mutable {a = 1; b = 1;}();
 
    std::cout << a << std::endl;  // 0
    std::cout << b << std::endl;  // 1
 
    return 0;
 }


また、一部の変数だけ指定して、残りは、キャプチャのデフォルトを指定することができる。

 #include <iostream>
 
 int main(int argc, const char *argv[])
 {
     int a = 0,
         b = 0,
         c = 0,
         d = 0;
 
     // a, bを参照、それ以外はコピー
     [=, &a, &b]() mutable {}();
 
     // a, bはコピー、それ以外は参照
     [&, a, b]() mutable {}();
 
     return 0;
 }


また、以下のように、デフォルトと同じキャプチャは指定できない。

 int a = 0,
     b = 0;
 
 [&, &a]{}();  // エラー
 [=, a]{}();   // エラー
 [a, a]{}();   // エラー


thisポインタとキャプチャ

thisポインタは、ポインタであるため、キャプチャがコピーでも参照でも、以下のように変数iは変更される。

 #include <iostream>
 
 typedef struct tag_S
 {
    int  i;
    void f()
    {
       [=]{this->i = 1;}();
    }
 }S;
 
 int main(int argc, char const* argv[])
 {
    S s;
    s.f();
 
    return 0;
 }


ラムダ式を返す

std::functionを使用することで、ラムダ式を関数から返すことができる。
キャプチャがコピーであるため、変数strは破棄されない。

 #include <iostream>
 #include <string>
 #include <functional>
 
 std::function<void()> f()
 {
    std::string str = "Hoge";
 
    return [=]{std::cout << str << std::endl;};
 }
 
 int main(int argc, const char *argv[])
 {
    auto func = f();
    func();  // 代入して呼び出し
 
    f()();   // 代入せずに呼び出し
 
    return 0;
 }