ラムダ式 (lambda expression) (c++11)

c++ のラムダ式について記載します。

 

 

 

1. ラムダ式の基本

ラムダ式は、簡潔に関数オブジェクトを記述する機能です。
以下のように記述します。

[ キャプチャー ] ( 仮引数リスト ) -> 戻り値の型 { 複合文 }

それぞれの役割と記述法は以下の通りです。

 

▷キャプチャー

キャプチャーには、ラムダ式から参照するオブジェクトを指定します。ラムダ式はキャプチャーで指定しない限り、ラムダ式が定義されたスコープで宣言されているオブジェクトを使用できません。

キャプチャーには以下の2種類があり、オブジェクトごとに指定できます。

●参照キャプチャー    ⇒  オブジェクトが参照で渡される
●コピーキャプチャー   ⇒  オブジェクトのコピーが渡される

 

▽キャプチャの記述例
記述 結果
[=] すべてのオブジェクトをコピーキャプチャ。メンバ関数内であれば、this もキャプチャされる。
[&] すべてのオブジェクトを参照キャプチャ。
[v] オブジェクト v をコピーキャプチャ。
[&v] オブジェクト v を参照キャプチャ。
[=,&v] オブジェクト v を参照キャプチャ、それ以外をコピーキャプチャ。
[&,v] オブジェクト v をコピーキャプチャ、それ以外を参照キャプチャ。

 

たとえば、オブジェクト a と b をコピーキャプチャ、 c と d を参照キャプチャする場合は、以下のように記述します。

[ a, b, &c, &d ]

ラムダ式の関数呼び出し演算子は、仮引数リストの後ろに mutable を指定していない限り、 const 修飾されており、コピーキャプチャしたオブジェクトの書き換えをできません。

ラムダ式の中でコピーキャプチャしたオブジェクトの書き換えを行いたい場合、仮引数リストに続いて、 mutable を指定する必要があります。

 

int i;
[i]()-> void { i = 1; };             // エラー:ラムダ式の中で、コピーキャプチャオブジェクトを変更できない
[i]() mutable -> void { i = 1; };    // OK:mutable を指定しているので変更可能

 

参照キャプチャしたオブジェクトの cv属性 は引き継がれます。そのため、const宣言されたオブジェクトを参照キャプチャーした場合、ラムダ式の中でオブジェクトの値を変更することはできません。

 

▷仮引数リスト(省略可能)

仮引数リストは、非メンバ関数宣言の仮引数リストと同じです。たとえば int型 の仮引数 i と float型 の仮引数 f をとるラムダ式の場合、仮引数リストは以下のようになります。

( int i, float f)

仮引数リストを省略した場合は、仮引数なし、つまり () と同じです。

 

▷戻り値の型(省略可能)

ラムダ式の戻り値の型を指定します。

戻り値の型を省略した場合、複合文が単一の return 式のみで構成されていれば、その return 式から推測されます。それ以外の場合は void と推論されます。

 

▷複合文

ラムダ式で行う処理を記述します。

たとえば、 int型 の仮引数を取り、キャプチャしたオブジェクト x との積を返すラムダ式は以下のとおりです。

 

[x](int v)->int { return v * x; };

 

上記のラムダ式をオブジェクトxが宣言されている関数 func で使用してみます。
ラムダ式は、オブジェクト lamda に代入したところで呼び出しています。

 

int func( int data ){
    int x = 5 ;
    auto lambda = [x](int v)->int { return x*v; };  // ラムダ式の定義
    int result = lambda(data);  // ラムダ式の実行

    return result;
}

 

2. ラムダ式をその場ですぐに実行する方法

以下のように記述することで、宣言したラムダ式をその場で呼び出せます。

// 例1
[]{ std::cout << "This sample is lambda function" << std::endl; }();

// 例2
int result = [](int l, int r)->int { return l * r; }(2,5);

 

3. ラムダ式をオブジェクトへ登録する

ラムダ式は、以下のように関数オブジェクトとしてオブジェクトへ代入できます。

// 引用元 https://msdn.microsoft.com/ja-jp/library/dd293599.aspx
// declaring_lambda_expressions1.cpp
// compile with: /EHsc /W4
#include <functional>
#include <iostream>

int main()
{

    using namespace std;

    // Assign the lambda expression that adds two numbers to an auto variable.
    auto f1 = [](int x, int y) { return x + y; };

    cout << f1(2, 3) << endl;

    // Assign the same lambda expression to a function object.
    function<int(int, int)> f2 = [](int x, int y) { return x + y; };

    cout << f2(3, 4) << endl;
}

 

何もキャプチャーしないラムダ式は、関数ポインタへ代入することもできます。

int (*ptr_to_lambda)(int,int) = [](int l, int r)->int { return l * r; };
int result = ptr_to_lambda(2,5);

 

4. ラムダ式をコールバック関数として使用する

アルゴリズムに分類される標準関数を使用する場合、引数としてコールバック関数を渡す必要がある場合があります。
ラムダ式を用いることで、このコールバック関数の宣言をローカル関数内で記述することができます。
反復利用しない関数を不要に増やすことなく実装が可能になりました。

#include <stdio.h> 
#include <stdlib.h> 
#include <tchar.h>
#include <iostream> 
#include <string> 
#include <algorithm>

using namespace std;

/*==========================*/ 
/* n で割り切れる要素を探す */ 
/*==========================*/
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int ar[] = { 9, 7, 5, 3, 1, -2, -4, -6, -8, 0 }; 
    int n = 4;

    // この行の 黄色部分 がラムダ式 !!
    int* found = std::find_if(ar, ar+10,  [n](int x) { return x % n == 0;} );

    if ( found != ar+(sizeof(ar)/sizeof(ar[0])) ){ 
        std::cout << n << " で割り切れる数 " << *found << " が見つかりました。" << std::endl; 
    }

    cout << "HIT [ENTER] KEY !! " ; 
    { 
        string str;
        getline( cin, str ); 
    } 
    return EXIT_SUCCESS; 
}

 

参考:

 

5. メンバー変数をラムダ式で使用する

ラムダ式内でメンバー変数を使用するには、キャプチャー対象として メンバー変数ではなく this を表記する、または一旦ローカル変数でメンバー変数を受けてそのローカル変数をキャプチャーする、というような方法をとる必要があります。

 

[評価環境]

コンパイラ : Visual Studio 2019 pro., Version 16.3.10
OS: Windows10 home, Version 1909

 

こちらは this をキャプチャーする例です。

[プログラムソース: "test_lambda_01.cpp"]

#include <iostream>

class Foo
{
private:
    int _x;

public:
    // Ctor
    Foo(int x)
        : _x(x)
    {}

    void print()
    {
        std::cout << "(1) " << _x << std::endl;

        [this] {
            std::cout << "(2) " << _x << std::endl;
            _x++;                                                      // thisのコピーキャプチャーで _x を変更しても...
        }();

        std::cout << "(3) " << _x << std::endl;         // this._x へちゃんと反映されています。
    }
};

int main()
{
    Foo foo(1);
    foo.print();
}

 

こちら、同じ内容ですが、メンバー変数を一旦ローカル変数で受けてからローカル変数をキャプチャーする例です。

[プログラムソース: "test_lambda_02.cpp"]

#include <iostream>

class Foo
{
private:
    int _x;

public:
    Foo(int x)
        : _x(x)
    {}

    void print()
    {
        std::cout << "(1) " << _x << std::endl;

        auto &x = _x;                                // 一旦メンバー変数をローカル変数で受けて...

        [&x] {                                           // この例では x を参照キャプチャーして使ってます。
            std::cout << "(2) " << x << std::endl;
            x++;                                        // thisのコピーキャプチャーで _x を変更しても...
        }();

        std::cout << "(3) " << _x << std::endl;         // this._x へちゃんと判定されています。
    }
};

int main()
{
    Foo foo(1);
    foo.print();
}

 

上記の2例について、いずれも同じ結果になります。

[実行結果]

実行結果

 

 

ライセンス

本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。

The MIT License (MIT)

  Copyright 2011-2019 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 

参考

 


 

記載

2019-12-01 - 「5. メンバー変数をラムダ式で使用する」を追加
2015-06-09 - 全面更新
2013-01-06 - 説明更新
2011-08-09 - 新規作成