# ラムダ式

実は以下の形の関数は、「関数」ではない。

auto function = []( auto value ) { return value } ;

これはラムダ式と呼ばれるC++の機能で、関数のように振る舞うオブジェクトを作るための式だ。

# 基本

ラムダ式の基本の文法は以下のとおり。

[](){} ;

これを細かく分解すると以下のようになる。

[]  // ラムダ導入子
()  // 引数リスト
{}  // 複合文
;   // 文末

ラムダ導入子はさておく。

引数リストは通常の関数と同じように型名と名前を書ける。

void f( int x, double d ) { }

[]( int x, double d ) { } ;

ラムダ式では、引数リストautoキーワードが使える。

[]( auto x ) { } ;

このように書くとどんな型でも受け取れるようになる。

int main()
{
    auto f = []( auto x )
    { std::cout << x ; } ;

    f(0) ; // int
    f(1.0) ; // double
    f("hello"s) ; // std::string
}

複合文{}だ。この{}の中に通常の関数と同じように複数の文を書くことができる。

[]()
{
    std::cout << "hello"s ;
    int x = 1 + 1 ;
} ;

最後の文末の最後に付けるセミコロンだ。これは"1+1 ;"とするのと変わらない。"1+1""[](){}"で、を使うことができる。だけが入ったを専門用語では式文と呼ぶが特に覚える必要はない。

1 + 1 ; // OK、式文
[](){} ; // OK、式文

ラムダ式なので式文の中に書くことができる。

ラムダ式なので、そのまま関数呼び出しすることもできる。

void f( std::string x )
{
    std::cout << x ;
}

int main()
{
    f( "hello"s ) ;
    []( auto x ){ std::cout << x ; }( "hello"s ) ;
}

これはわかりやすくインデントすると以下のようになる。

f               // 関数
( "hello"s ) ;  // 関数呼び出し

// ラムダ式
[]( auto x ){ std::cout << x ; }
( "hello"s ) ;  // 関数呼び出し

ラムダ式が引数を1つも取らない場合、引数リストは省略できる。

// 引数を取らないラムダ式
[](){} ;
// 引数リストは省略できる
[]{} ;

ラムダ式の戻り値の型はreturn文から推定される。

// int
[]{ return 0 ; } ;
// double
[]{ return 0.0 ; } ;
// std::string
[]{ return "hello"s ; } ;

return文で複数の型を返した場合は推定ができないのでエラーになる。

[]( bool b )
{
    if ( b )
        return 0 ;
    else
        return 0.0 ;
} ;

戻り値の型を指定したい場合は引数リストのあとに->を書き、型名を書く。

[]( bool b ) -> int
{
    if ( b )
        return 0 ;
    else
        // doubleからintへの変換
        return 0.0 ;
} ;

戻り値の型の推定は通常の関数も同じだ。

// int
auto f() { return 0 ; }

// 戻り値の型の明示的な指定
auto f() -> int { return 0 ; }

# キャプチャー

ラムダ式は書かれている関数のローカル変数を使うことができる。これをキャプチャーという。キャプチャーは通常の関数にはできないラムダ式の機能だ。

void f()
{
    // ローカル関数
    auto message = "hello"s ;

    [=](){ std::cout << message ; } ;
}

キャプチャーにはコピーキャプチャーリファレンスキャプチャーがある。

# コピーキャプチャー

コピーキャプチャーは変数をコピーによってキャプチャーする。

コピーキャプチャーをするには、ラムダ式[=]と書く。

int main()
{
    int x = 0 ;
    // コピーキャプチャー
    [=]{ return x ; } ;
}

コピーキャプチャーした変数はラムダ式の中で変更できない。

int main()
{
    int x = 0 ;
    // エラー
    [=]{ x = 0 ; } ;
}

変更できるようにする方法もあるのだが、通常は使われない。

# リファレンスキャプチャー

リファレンスキャプチャーは変数をリファレンスによってキャプチャーする。

リファレンスを覚えているだろうか。リファレンスは初期化時の元の変数を参照する変数だ。

int main()
{
    int x = 0 ;
    // 通常の変数
    int y = x ;

    // 変数を変更
    y = 1 ;
    // xの値は変わらない

    // リファレンス
    int & ref = x ;

    // リファレンスを変更
    ref = 1 ;
    // xの値が変わる
}

リファレンスキャプチャーを使うには、ラムダ式[&]と書く。

int main()
{
    int x = 0 ;
    [&] { return x ; } ;
}

リファレンスキャプチャーした変数をラムダ式の中で変更すると、元の変数が変更される。

int main()
{
    int x = 0 ;
    auto f = [&]{ ++x ; } ;

    f() ; // x == 1
    f() ; // x == 2
    f() ; // x == 3
}

ラムダ式についてはまだいろいろな機能があるが、本書での解説はここまでとする。