「ラムダ式」の版間の差分

提供: C++入門
移動: 案内検索
(ラムダ式を使ったコードのコンパイル)
(概要)
 
(同じ利用者による、間の1版が非表示)
行16: 行16:
 
* [[ラムダ式]]は、[[std::function]]に代入できます。
 
* [[ラムダ式]]は、[[std::function]]に代入できます。
 
* [[ラムダ式]]は、STLの引数に使用できます。関数の引数として、[[ラムダ式]]を直接書けます。
 
* [[ラムダ式]]は、STLの引数に使用できます。関数の引数として、[[ラムダ式]]を直接書けます。
 +
** 以前は、関数をその場で定義できず、別途定義したものを利用していました。
 
* [[ラムダ式]]は、ローカル変数をキャプチャできます。
 
* [[ラムダ式]]は、ローカル変数をキャプチャできます。
* 型推論([[auto]]も利用できます。
+
* [[ラムダ式]]は、引数や戻り値の型推論([[auto]])も利用できます。
  
 
== ラムダ式を使ったコードのコンパイル ==
 
== ラムダ式を使ったコードのコンパイル ==

2014年8月27日 (水) 19:57時点における最新版

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

読み方

ラムダ式
らむだしき
lambda expresssion
らむだ えくすぷれっしょん
無名関数
むめいかんすう
anonymous function
あのにます ふぁんくしょん
nameless function
ねいむれす ふぁんくしょん
匿名関数
とくめいかんすう

概要

  • ラムダ式は、無名関数
  • ラムダ式は、関数オブジェクトで、ラムダ式は、生成されたクラスのoperator()になります。
  • ラムダ式は、std::functionに代入できます。
  • ラムダ式は、STLの引数に使用できます。関数の引数として、ラムダ式を直接書けます。
    • 以前は、関数をその場で定義できず、別途定義したものを利用していました。
  • ラムダ式は、ローカル変数をキャプチャできます。
  • ラムダ式は、引数や戻り値の型推論(auto)も利用できます。

ラムダ式を使ったコードのコンパイル

C++では、ラムダ式は、C++11からサポートされました。 新しいg++コンパイラを使用し、c++11を有効にしてください。

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

ラムダ式の仕様

C++11C++14で仕様が違います。C++14版は、より高度になっています。

C++のラムダ式の書き方

最小のラムダ式の定義

これだけで、定義したことになります。

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

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

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

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

ラムダ式を呼び出すには、関数と同じように () を使用します。

int main(int argc, char *argv[]) {
	[](){}();
	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, char const* argv[])
{
        f( []{ std::cout<<"Hello world"<<std::endl;} );
        return 0;
}

ラムダ式に引数を与える

#include <iostream>
#include <string>
using namespace std;
int main(int argc, char const* argv[])
{
        [](string const & str) // 引数の定義
        {       // 関数本体
                cout << str << endl;
        }
        ( "I am Argument!");    // 関数呼び出しと引数
        return 0;
}

ラムダ式で戻り値を返す

ラムダ式は、戻り値を返せます。

  • 1つ目は、明示的に戻り値の型を定義していません。
  • 2つ目は、明示的に戻り値の型を定義しています。アロー演算子の記法([]()->型)で表現します。
#include <iostream>
using namespace std;
int main(int argc, char const* argv[])
{
        auto a = [] { return 0;} (); // 戻り値を推測させる
        auto b = [] () -> float { return 3.14; } (); //明示的に戻り値を定義
 
        cout << a << endl;
        cout << b << endl;
        return 0;
}
% g++49 -std=c++11 lambda_return1.cpp
% ./a.out
0
3.14

変数のキャプチャ

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

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

参照の場合の代入

参照で上書きをしてみます。

#include <iostream>
#include <string>
using namespace std;
int main(int argc, char const* argv[])
{
        string s1 = "I am s1";
        [&] { s1 = "Overwrite s1!"; } ();
        cout << s1 << endl;
        return 0;
}
% g++49 -std=c++11 lambda_capture2.cpp
% ./a.out
Overwrite s1!

コピーの場合の代入

コピーしているときに、上書きしようとすると C++11 では、コンパイラがエラーを引き起こします。

#include <iostream>
#include <string>
using namespace std;
int main(int argc, char const* argv[])
{
        string x = "I am string";
        [&] { cout << x << endl; } (); //参照
        [=] { cout << x << endl; } (); //コピー
 
        string s1 = "I am s1";
        string s2 = "I am s2";
        [&] { s1 = "Overwrite s1!"; } ();
        [=] { s2 = "Overwrite s2!"; } ();
 
        cout << s1 << endl;
        cout << s2 << endl;
        return 0;
}

コンパイルエラーになりました。

% g++49 -std=c++11 lambda_capture1.cpp
lambda_capture1.cpp: In lambda function:
lambda_capture1.cpp:13:11: error: passing 'const string {aka const
std::basic_string<char>}' as 'this' argument of 'std::basic_string<_CharT,
_Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const
_CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc =
std::allocator<char>]' discards qualifiers [-fpermissive]
  [=] { s2 = "Overwrite s2!"; } ();

mutable

コピーの場合、変数を書き換えられませんが、mutableを使用することで、変数を変更できます。しかし、コピーのため、元の変数の値は、変更できません。 mutableを指定したとき、ラムダ式の引数リストのカッコは、省略できません。

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

コンパイルエラーは、発生しなくなりました。

% g++49 -std=c++11 lambda_capture3.cpp
% ./a.out
I am s1

mutableを指定したとき、戻り値の指定は、以下のようになります。

[]() mutable -> {};

キャプチャの指示

最初の[]には、変数ごとにキャプチャの方法(コピーか参照か)を指定できます。

#include <iostream>
using namespace std;
int main(int argc, char const* argv[])
{
        int a = 0, b = 0;
 
        [a, &b] () mutable { a = 1; b = 1; } ();
        cout << a << endl;
        cout << b << endl;
        return 0;
}
% ./a.out
0
1

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

#include <iostream>
using namespace std;
int main(int argc, char const* 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>
using namespace std;
struct S {
        int     i;
        void    f() {
                [=]{this->i = 1;}();
        }
};
int main(int argc, char const* argv[])
{
        S s;
        s.f();
        return 0;
}

ラムダ式を返す

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

#include <iostream>
#include <string>
#include <functional>
using namespace std;
std::function< void() > f() {
        string str("Hoge");
        return [=] { cout << str << endl; };
}
int main(int argc, char const* argv[])
{
        auto func = f();
 
        func();	// 代入して呼び出し
 
        f()();	// 代入せずに、呼び出し
        return 0;
}
% g++49 -g -ggdb -fPIC -std=c++11 \
-I/usr/local/lib/gcc49/include/c++ \
-I/usr/local/lib/gcc49/include/ \
-L/usr/local/lib/gcc49 \
-Wl,-rpath=/usr/local/lib/gcc49/ lambda_return2.cpp
% ./a.out
Hoge
Hoge

関連項目