「C++の基礎 - ラムダ式」の版間の差分
(ページの作成:「== 概要 == ラムダ式(無名関数(anonymous function, nameless function)、匿名関数)とは、関数を定義するための記法、文法のことである。<b…」) |
細 (文字列「source lang」を「syntaxhighlight lang」に置換) |
||
(同じ利用者による、間の4版が非表示) | |||
15行目: | 15行目: | ||
<br><br> | <br><br> | ||
== ラムダ式の文法 == | |||
<syntaxhighlight lang="c++"> | |||
< | |||
int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||
{ | { | ||
28行目: | 27行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br><br> | ||
< | == 最小のラムダ式の定義 == | ||
<syntaxhighlight lang="c++"> | |||
int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||
{ | { | ||
38行目: | 38行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
引数は省略可能であるため、さらに省略できる。<br> | 引数は省略可能であるため、さらに省略できる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||
{ | { | ||
48行目: | 48行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br><br> | ||
== 最小のラムダ式の呼び出し == | |||
ラムダ式を呼び出すには、関数呼び出し式()を使用する。<br> | ラムダ式を呼び出すには、関数呼び出し式()を使用する。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||
{ | { | ||
58行目: | 59行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br><br> | ||
== サンプルコード == | |||
===== ラムダ式で書くHello World ===== | ===== ラムダ式で書くHello World ===== | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
70行目: | 73行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== ラムダ式を変数に代入する ===== | ===== ラムダ式を変数に代入する ===== | ||
ラムダ式を変数に代入することができる。関数ポインタのような感覚で呼び出せる。<br> | ラムダ式を変数に代入することができる。関数ポインタのような感覚で呼び出せる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
84行目: | 87行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== ラムダ式を関数に渡す ===== | ===== ラムダ式を関数に渡す ===== | ||
ラムダ式を別の関数に渡すことができる。(コールバック関数)<br> | ラムダ式を別の関数に渡すことができる。(コールバック関数)<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
103行目: | 106行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== ラムダ式に引数を与える ===== | ===== ラムダ式に引数を与える ===== | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
#include <string> | #include <string> | ||
120行目: | 123行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== ラムダ式で戻り値を返す ===== | ===== ラムダ式で戻り値を返す ===== | ||
126行目: | 129行目: | ||
1つ目は、明示的に戻り値の型を定義していない。<br> | 1つ目は、明示的に戻り値の型を定義していない。<br> | ||
2つ目は、明示的に戻り値の型を定義している。アロー演算子の記法([]()->型)で表現している。<br> | 2つ目は、明示的に戻り値の型を定義している。アロー演算子の記法([]()->型)で表現している。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
139行目: | 142行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== 変数のキャプチャ ===== | ===== 変数のキャプチャ ===== | ||
ラムダ式は、式が定義される関数のスコープの変数をキャプチャできる。<br> | ラムダ式は、式が定義される関数のスコープの変数をキャプチャできる。<br> | ||
JavaScriptでいうクロージャが、スコープにある変数を参照できることをイメージすること。<br> | JavaScriptでいうクロージャが、スコープにある変数を参照できることをイメージすること。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
#include <string> | #include <string> | ||
156行目: | 159行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== 参照の場合の代入 ===== | ===== 参照の場合の代入 ===== | ||
参照での代入とコピーでの代入を行う。<br> | 参照での代入とコピーでの代入を行う。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
#include <string> | #include <string> | ||
177行目: | 180行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== mutable ===== | ===== mutable ===== | ||
コピーでの代入では、変数を書き変えることができないが、mutableを使用することで、ラムダ式の中だけ変数を書き変えることができる。 | コピーでの代入では、変数を書き変えることができないが、mutableを使用することで、ラムダ式の中だけ変数を書き変えることができる。<br> | ||
しかし、コピーのため、元の変数の値は変更できない。 | しかし、コピーのため、元の変数の値は変更できない。<br> | ||
mutableを指定した時、ラムダ式の引数リストの括弧は省略できない。 | mutableを指定した時、ラムダ式の引数リストの括弧は省略できない。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
#include <string> | #include <string> | ||
196行目: | 199行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
また、mutableを指定した時、明示的に戻り値を指定する場合は、以下のようにする。<br> | また、mutableを指定した時、明示的に戻り値を指定する場合は、以下のようにする。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
auto dRet = []() mutable -> double {return 3.14;}; | auto dRet = []() mutable -> double {return 3.14;}; | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== キャプチャの指示 ===== | ===== キャプチャの指示 ===== | ||
ラムダキャプチャ[]には、変数ごとにキャプチャの方法(コピーまたは参照)を指定できる。<br> | ラムダキャプチャ[]には、変数ごとにキャプチャの方法(コピーまたは参照)を指定できる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
220行目: | 224行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
また、一部の変数だけ指定して、残りは、キャプチャのデフォルトを指定することができる。<br> | また、一部の変数だけ指定して、残りは、キャプチャのデフォルトを指定することができる。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
241行目: | 245行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
また、以下のように、デフォルトと同じキャプチャは指定できない。<br> | また、以下のように、デフォルトと同じキャプチャは指定できない。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
int a = 0, | int a = 0, | ||
b = 0; | b = 0; | ||
251行目: | 255行目: | ||
[=, a]{}(); // エラー | [=, a]{}(); // エラー | ||
[a, a]{}(); // エラー | [a, a]{}(); // エラー | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== thisポインタとキャプチャ ===== | ===== thisポインタとキャプチャ ===== | ||
thisポインタは、ポインタであるため、キャプチャがコピーでも参照でも、以下のように変数iは変更される。<br> | thisポインタは、ポインタであるため、キャプチャがコピーでも参照でも、以下のように変数iは変更される。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
274行目: | 278行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
===== ラムダ式を返す ===== | ===== ラムダ式を返す ===== | ||
std::functionを使用することで、ラムダ式を関数から返すことができる。<br> | std::functionを使用することで、ラムダ式を関数から返すことができる。<br> | ||
キャプチャがコピーであるため、変数strは破棄されない。<br> | キャプチャがコピーであるため、変数strは破棄されない。<br> | ||
< | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
#include <string> | #include <string> | ||
300行目: | 304行目: | ||
return 0; | return 0; | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C++]] | [[カテゴリ:C++]] |
2021年11月24日 (水) 17:52時点における最新版
概要
ラムダ式(無名関数(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;
}