ラムダ式
ラムダ式 (lambda expression, 無名関数(anonymous function, nameless function)、匿名関数)とは、関数を定義するための記法、文法です。
読み方
- ラムダ式
- らむだしき
- lambda expresssion
- らむだ えくすぷれっしょん
- 無名関数
- むめいかんすう
- anonymous function
- あのにます ふぁんくしょん
- nameless function
- ねいむれす ふぁんくしょん
- 匿名関数
- とくめいかんすう
目次
概要
- ラムダ式は、無名関数
- ラムダ式は、関数オブジェクト
ラムダ式を使ったコードのコンパイル
C++では、ラムダ式は、C++11からサポートされました。 新しいg++コンパイラを使用し、c++11を有効にしてください。
g++49 -std=c++11 lambda.cpp
ラムダ式の仕様
C++11とC++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
関連項目
ツイート