C++のlambda式
28534 ワード
Lambda式 1. Lambda式とは? 2. Lambda式の構造 (1)パラメータリスト (2)戻り値タイプ (3)キャプチャリスト によって取得された2つのタイプ によって取得された2つのモード 明示的キャプチャと暗黙的キャプチャを混合する (4)可変lambda 3. Lambda式の用途 1.Lambda式とは?
Lambda式を紹介する前に、次の例を見てみましょう.
そう、カッコ3大企業(中かっこ、小かっこ、大かっこ)はlambda式です.このlambda式には関数名もパラメータも関数体もありませんが、この3つのカッコはlambda式の3つの非常に重要な構成部分に対応しています.
C++のlambda式は匿名関数と見なすことも,インライン関数として用いることもできる.インライン関数の意味はここでは議論されないが,lambda式は匿名関数として多くの場所で有用である.
さらにlambda式は呼び出し可能なオブジェクトです.C++の呼び出し可能なオブジェクトには、関数、関数ポインタ、および「()」演算子が再ロードされたクラスなどがあります.
2.Lambda式の構造
C++のlambda式の構造は次のようになります.
[capture_list] (parameter_list) mutable exception -> return_type {function_body}
ここで、太字の2つは必須で、その他の部分はオプションです.各構成部分の具体的な意味は:1.
(1)パラメータリスト
C++のlambda式のパラメータリストは、通常の関数のパラメータリストと似ています.異なる点は次のとおりです.
1.lambda式のパラメータタイプはauto(C++14でサポート開始)に設定できます.コンパイラは入力した値に基づいて判断しますが、通常の関数のパラメータにはパラメータタイプが指定されている必要があります.2.lambda式のパラメータはデフォルトのパラメータではありません.(この点はC++11以降は制限されません)
(2)戻り値タイプ
ほとんどの場合、lambda式の戻り値はコンパイラによって推測されるため、lambda式の戻り値タイプを指定する必要はありません.最後の値でタイプを返す方法でlambda式の戻りタイプを設定できます.
(3)取得リスト
キャプチャリストの用途を説明するには、次の例を参照してください.
Lambda式は、ローカル変数をキャプチャリストに含めることで、使用するローカル変数を示します.ここでlambda式はmian関数のローカル変数valを使用する必要があるため、valをキャプチャリストに入れます.注意:lambda式は静的変数または外部変数を直接使用できます.ローカル非静的変数を使用する必要がある場合は、キャプチャが必要です.
キャプチャされた2つのタイプ
1.明示的な取得:非静的ローカル変数を使用する必要がある場合は、明示的に取得し、取得リストに含める必要があります.2.暗黙的なキャプチャ:静的変数と外部変数の場合、システムは私たちの使用状況に基づいてキャプチャする必要がある変数を推定することができるので、明示的にキャプチャリストに入れる必要はありません.
注意:暗黙的なキャプチャでは、キャプチャモードを指定する必要があります.キャプチャの2つのモードについては、以下で説明する.
キャプチャされた2つのモード
キャプチャには、値キャプチャと参照キャプチャの2つの方法があります.次の2つの形式は、値取得と参照取得をそれぞれ表します.1.
明示的なキャプチャと暗黙的なキャプチャのブレンド
非静的ローカル変数、静的変数、外部変数などを同時にキャプチャする必要がある場合は、次のように明示的および暗黙的にキャプチャする必要があります.
取得リスト
取得タイプ
[]
空のキャプチャ
[identifier_list]
明示的な値の取得
[&identifier_list]
明示的参照の取得
[=]
暗黙値の取得
[&]
暗黙参照の取得
[=, identifier_list]
ブレンドキャプチャ、暗黙値キャプチャ、明示的参照キャプチャ
[&, identifier_list]
ブレンドキャプチャ、暗黙参照キャプチャ、明示的値キャプチャ
最後の2つはブレンドキャプチャであり、暗黙的なキャプチャが値キャプチャの場合、明示的なキャプチャは参照キャプチャのみとなります.暗黙的なキャプチャが参照キャプチャの場合、明示的なキャプチャは値キャプチャのみです.
(4)可変lambda
可変lambdaを紹介する前に定数関数とは何かを見てみましょう.C++では、定数メンバー関数ではメンバー変数の値を変更できません.次の例を示します.
上記のプログラムではmodifyメンバー関数はconstで修飾され、定数メンバー関数であり、メンバー変数func_を変更できません.numの値.そのため、プログラムはコンパイル時に間違っています.
このlambda式の例を見てみましょう.
ここでは暗黙的な値キャプチャを採用したが,lambda関数もconst関数であるため,valの値を変更することはできない.そのため、このプログラムはコンパイル時にもエラーを報告します.
解決方法はmutableキーワードです.
これでlambda関数でvalの値を変更できます.しかし、ここでは値伝達であるため、lambda関数の変更は実際にはvalのコピー、すなわち一時変数であることに注意してください.プログラムの実行結果は次のとおりです.
結果からvec中の要素はすべて10になり,valの値は依然として10であることが分かった.
3.Lambda式の使い道
Lambda式の重要な役割は、STLライブラリのさまざまなアルゴリズムに合わせて、構造をより簡潔でコンパクトにし、機能をより強力にすることです.以下にいくつかの簡単な例を挙げる:(1)for_each()に合わせる
(2)カスタムソート
(3)カウント
Lambda式を紹介する前に、次の例を見てみましょう.
int main() {
[](){};
return 0;
}
そう、カッコ3大企業(中かっこ、小かっこ、大かっこ)はlambda式です.このlambda式には関数名もパラメータも関数体もありませんが、この3つのカッコはlambda式の3つの非常に重要な構成部分に対応しています.
C++のlambda式は匿名関数と見なすことも,インライン関数として用いることもできる.インライン関数の意味はここでは議論されないが,lambda式は匿名関数として多くの場所で有用である.
さらにlambda式は呼び出し可能なオブジェクトです.C++の呼び出し可能なオブジェクトには、関数、関数ポインタ、および「()」演算子が再ロードされたクラスなどがあります.
2.Lambda式の構造
C++のlambda式の構造は次のようになります.
[capture_list] (parameter_list) mutable exception -> return_type {function_body}
ここで、太字の2つは必須で、その他の部分はオプションです.各構成部分の具体的な意味は:1.
capture_list
はリストをキャプチャし、リスト内のローカル変数をlambda式で使用することができる.2.parameter_list
パラメータのリストで、関数のパラメータのリストに相当します.3.mutable
は、取得リストの変数が変更されるかどうかを示すために使用される.4.exception
異常を宣言します.5.return_type
は、値タイプを返します.(1)パラメータリスト
C++のlambda式のパラメータリストは、通常の関数のパラメータリストと似ています.異なる点は次のとおりです.
1.lambda式のパラメータタイプはauto(C++14でサポート開始)に設定できます.コンパイラは入力した値に基づいて判断しますが、通常の関数のパラメータにはパラメータタイプが指定されている必要があります.2.lambda式のパラメータはデフォルトのパラメータではありません.(この点はC++11以降は制限されません)
int main() {
auto lam = [](auto elem1 ,auto elem2) {return elem1 > elem2 ? elem1 : elem2; };
cout << "max(2,3)=" << lam(2, 3) << endl;
return 0;
}
max(2,3)=3
(2)戻り値タイプ
ほとんどの場合、lambda式の戻り値はコンパイラによって推測されるため、lambda式の戻り値タイプを指定する必要はありません.最後の値でタイプを返す方法でlambda式の戻りタイプを設定できます.
int main() {
auto lam = [](int elem) -> int {return -elem; };
cout << lam(3) << endl;
return 0;
}
-3
(3)取得リスト
キャプチャリストの用途を説明するには、次の例を参照してください.
int main() {
int val = 10;
auto lam = [val](int elem){return val-elem; };
cout << "10 - 3 = " << lam(3) << endl;
return 0;
}
Lambda式は、ローカル変数をキャプチャリストに含めることで、使用するローカル変数を示します.ここでlambda式はmian関数のローカル変数valを使用する必要があるため、valをキャプチャリストに入れます.注意:lambda式は静的変数または外部変数を直接使用できます.ローカル非静的変数を使用する必要がある場合は、キャプチャが必要です.
キャプチャされた2つのタイプ
1.明示的な取得:非静的ローカル変数を使用する必要がある場合は、明示的に取得し、取得リストに含める必要があります.2.暗黙的なキャプチャ:静的変数と外部変数の場合、システムは私たちの使用状況に基づいてキャプチャする必要がある変数を推定することができるので、明示的にキャプチャリストに入れる必要はありません.
int main() {
int val = 10;
vector<int> vec = { 1, 2, 3, 4, 5 };
for_each(vec.begin(), vec.end(), [=](int& elem) {elem+=val;});
return 0;
}
注意:暗黙的なキャプチャでは、キャプチャモードを指定する必要があります.キャプチャの2つのモードについては、以下で説明する.
キャプチャされた2つのモード
キャプチャには、値キャプチャと参照キャプチャの2つの方法があります.次の2つの形式は、値取得と参照取得をそれぞれ表します.1.
[=(identifier)]
値取得;2.[&(identifier)]
引用捕獲;明示的なキャプチャと暗黙的なキャプチャのブレンド
非静的ローカル変数、静的変数、外部変数などを同時にキャプチャする必要がある場合は、次のように明示的および暗黙的にキャプチャする必要があります.
取得リスト
取得タイプ
[]
空のキャプチャ
[identifier_list]
明示的な値の取得
[&identifier_list]
明示的参照の取得
[=]
暗黙値の取得
[&]
暗黙参照の取得
[=, identifier_list]
ブレンドキャプチャ、暗黙値キャプチャ、明示的参照キャプチャ
[&, identifier_list]
ブレンドキャプチャ、暗黙参照キャプチャ、明示的値キャプチャ
最後の2つはブレンドキャプチャであり、暗黙的なキャプチャが値キャプチャの場合、明示的なキャプチャは参照キャプチャのみとなります.暗黙的なキャプチャが参照キャプチャの場合、明示的なキャプチャは値キャプチャのみです.
(4)可変lambda
可変lambdaを紹介する前に定数関数とは何かを見てみましょう.C++では、定数メンバー関数ではメンバー変数の値を変更できません.次の例を示します.
class Func {
public:
int func_num = 0;
void modify(int val)const{ this->func_num = val;}
};
上記のプログラムではmodifyメンバー関数はconstで修飾され、定数メンバー関数であり、メンバー変数func_を変更できません.numの値.そのため、プログラムはコンパイル時に間違っています.
このlambda式の例を見てみましょう.
int main() {
int val = 10;
vector<int> vec = { 1, 2, 3, 4, 5 };
for_each(vec.begin(), vec.end(), [=](int& elem){elem += (--val); });
return 0;
}
ここでは暗黙的な値キャプチャを採用したが,lambda関数もconst関数であるため,valの値を変更することはできない.そのため、このプログラムはコンパイル時にもエラーを報告します.
解決方法はmutableキーワードです.
int main() {
int val = 10;
vector<int> vec = { 1, 2, 3, 4, 5 };
for_each(vec.begin(), vec.end(), [=](int& elem)mutable{ elem += (--val); });
for (auto i : vec) cout << i << " ";
cout << "
val = " << val;
return 0;
}
これでlambda関数でvalの値を変更できます.しかし、ここでは値伝達であるため、lambda関数の変更は実際にはvalのコピー、すなわち一時変数であることに注意してください.プログラムの実行結果は次のとおりです.
10 10 10 10 10
val = 10
結果からvec中の要素はすべて10になり,valの値は依然として10であることが分かった.
3.Lambda式の使い道
Lambda式の重要な役割は、STLライブラリのさまざまなアルゴリズムに合わせて、構造をより簡潔でコンパクトにし、機能をより強力にすることです.以下にいくつかの簡単な例を挙げる:(1)for_each()に合わせる
int main() {
vector<int> vec = { 1, 2, 3, 4, 5 };
for_each(vec.begin(), vec.end(), [](int& elem){ elem =elem*elem; });
for (auto i : vec) cout << i << " ";
return 0;
}
1 4 9 16 25
(2)カスタムソート
struct Point {
public:
int x, y;
Point(int x_val, int y_val) :x(x_val), y(y_val) {};
};
int main() {
Point p1(3, 2), p2(6, -2), p3(4, 4);
vector<Point> vec = {p1, p2, p3};
sort(vec.begin(), vec.end(),
[](Point elem1, Point elem2){
return elem1.x < elem2.x; });
print_vec(vec);
return 0;
}
(3,2) (4,4) (6,-2)
(3)カウント
int main() {
int num;
vector<int> vec = { 3, 2, 4, 4, 1, 5, 8, 2, 4 };
print_container(vec);
num=count(vec.begin(), vec.end(), 4);//count
cout << "N(element=4): " << num << endl;
num = count_if(vec.begin(), vec.end(),
[](int elem) {return elem > 4; });//count_if
cout << "N(element>4): " << num << endl;
num = count_if(vec.begin(), vec.end(),
[](int elem) {return elem % 4 == 0; });//count_if
cout << "N(element%4=0): " << num << endl;
return 0;
}
Element: 3 2 4 4 1 5 8 2 4
N(element=4): 3
N(element>4): 2
N(element%4=0): 4