C++新特性~lambda抜粋
Lambda関数と表現[編集]
C++11標準は匿名関数のサポートを提供し、『ISO/IEC 14882:2011』(C++11標準ドキュメント)ではlambda式と呼ばれている[10].lambda式には以下の形式がある.
四角カッコで囲まれたcaptureリストでlambda式の定義を開始する必要があります.
Lambda関数のパラメータテーブルは、通常の関数のパラメータテーブルよりも3つの制限があります.パラメータにデフォルト値があることはできません 可変長パラメータリスト を持つことはできない.無名パラメータ は使用できません.
lambda関数にパラメータがなく、mutable、exception、またはattribute宣言がない場合、パラメータの空のカッコは省略できます.ただし、mutable、exception、またはattribute宣言を指定する必要がある場合は、パラメータが空であってもカッコは省略できません.
関数体にreturn文が1つしかない場合、または戻り値タイプがvoidの場合、戻り値タイプ宣言は省略できます.
lambda関数の例は次のとおりです.
この無名関数の戻り値は
特に、sortおよびfindのようなC++標準ライブラリアルゴリズム関数を使用する場合、使用者は、アルゴリズム関数呼び出しの近傍で一時的な部分関数(述語関数、predicate functionとも呼ばれる)を定義することをしばしば望んでいる.言語自体が関数内部でクラスを定義できるため、関数オブジェクトの使用を考慮することができますが、これは通常、面倒で冗長であり、コードの流れを阻害します.また,標準C++では関数内部に定義されたクラスをテンプレートに使用することは許されないので,前述のやり方は不可能である.
C++11のlambdaへのサポートは上記の問題を解決することができる.
lambda関数は、次のように定義できます.
この無名関数の戻りタイプはdecltype(x+y)である.lambda関数が「return expression」の形式に合致する場合にのみ、その戻りタイプは無視されます.前述の場合、lambda関数は1つの文のみとすることができる.
より複雑な例では、戻りタイプは以下のように明確に指定できます(戻り値タイプは明示的に指定できます).
この例では、中間結果を格納するために一時的なパラメータzが作成される.一般的な関数と同様に、zの値は、次回この名前のない関数が再び呼び出されるまで保持されません(中間値は呼び出しの前後には存在しません).
lambda関数がvoidなどの戻り値を返さない場合、その戻りタイプは完全に無視され得る(戻り値を明示的に指定する必要はなく、
lambda関数と同じ役割ドメインを定義するパラメータ参照も使用できます.このようなパラメータセットは一般にclosure(閉パケット)と呼ばれる.
Lambda関数はlambda関数以外のautomatic storage durationを有する変数をキャプチャすることができる.関数体をこれらの変数の集合と合わせて閉パケットと呼ぶ.これらの外部変数は、lambda式を宣言するときに、四角カッコ
closureは次のように定義され、使用されます.
上記の例では、someList要素の合計を計算し、印刷することができます.パラメータtotalはlambda関数closureの一部であり、述語関数に参照で渡されるため、その値はlambda関数によって変更することができる.
参照されたシンボル&を使用しない場合、代表パラメータはlambda関数に値を伝達するように入力されます.使用者は,この表現法を用いてパラメータ伝達の方法,すなわち伝達値,または伝達参照を明確に区別できる.lambda関数は、std::functionオブジェクトに配置するなど、宣言された場所でその場で使用できないため、この場合,パラメータがclosureに参照でリンクされている場合は,無意味で危険な行為である.
lambda関数が定義された役割ドメインでのみ使用される場合は、stackに参照されているすべてのパラメータを表すlambda関数を[&]で宣言できます.すべてのパラメータは、参照によって入力されます.明示的に指定する必要はありません.
パラメータがlambda関数に伝達される方法は、実装によって変化する可能性があり、一般に、lambda関数がその役割ドメイン関数のstackポインタを保持し、これによって領域パラメータにアクセスすることが望ましい.
[&]ではなく[=]を使用すると、すべての参照を表すパラメータが値伝達で使用されます.
異なるパラメータの場合、値または参照は混和して使用できます.例えば、使用者は、すべてのパラメータを参照で使用することができますが、1つの値で使用されるパラメータがあります.
totalはlambda関数に参照を伝達する方式で伝達され、valueは伝達値である.
クラスの非静的メンバー関数で定義されたlambda式は、関数パラメータ ローカル宣言変数 クラスデータメンバー(関数がクラスに宣言された場合) 静的記憶期間を有する変数(例えばグローバル変数) 取得外部変数 明示的に取得された変数 暗黙的に取得された変数.デフォルトの取得モード(値または参照)を使用してアクセスします.
Lambda関数のデータ型は関数オブジェクトであり、保存時に
lambda関数のスナップ式が空の場合、通常の関数ポインタで格納または呼び出すことができます.例:
lambda関数がクラスのメンバー関数に定義されている場合、クラスオブジェクトの参照を使用して、内部のメンバーにアクセスできます.
これはlambda関数が作成した役割ドメインがSomeTypeのメンバー関数の内部にある場合にのみ動作します.
メンバー関数でオブジェクトを指すthisポインタは、lambda関数に明示的に入力する必要があります.そうしないと、メンバー関数のlambda関数では、オブジェクトのパラメータまたは関数は使用できません.
lambda関数が[&]または[=]の形式を使用する場合、thisはlambda関数で表示されます.
Lambda関数はコンパイラ依存型の関数オブジェクトです.このタイプの名前はコンパイラ自身でしか使用できません.使用者がlambda関数をパラメータとして入力する場合は、そのタイプがテンプレートタイプであるか、std::functionを作成してlambdaの値を取得する必要があります.autoキーワードを使用すると、lambda関数を格納できます.
C++11標準は匿名関数のサポートを提供し、『ISO/IEC 14882:2011』(C++11標準ドキュメント)ではlambda式と呼ばれている[10].lambda式には以下の形式がある.
[capture] (parameters) mutable exception attribute -> return_type { body }
四角カッコで囲まれたcaptureリストでlambda式の定義を開始する必要があります.
Lambda関数のパラメータテーブルは、通常の関数のパラメータテーブルよりも3つの制限があります.
lambda関数にパラメータがなく、mutable、exception、またはattribute宣言がない場合、パラメータの空のカッコは省略できます.ただし、mutable、exception、またはattribute宣言を指定する必要がある場合は、パラメータが空であってもカッコは省略できません.
関数体にreturn文が1つしかない場合、または戻り値タイプがvoidの場合、戻り値タイプ宣言は省略できます.
[capture](parameters){body}
lambda関数の例は次のとおりです.
[](int x, int y) { return x + y; } // return
[](int& x) { ++x; } // return -> lambda void
[]() { ++global_x; } // ,
[]{ ++global_x; } // ,()
この無名関数の戻り値は
decltype(x+y)
(上記の第1の例では)である.lambda関数体の形式がreturn expression
である場合、あるいは何でも返すたびに、あるいはすべての戻り文がdecltype
で同じタイプを検出できる場合、戻り値タイプを省略することができる.特に、sortおよびfindのようなC++標準ライブラリアルゴリズム関数を使用する場合、使用者は、アルゴリズム関数呼び出しの近傍で一時的な部分関数(述語関数、predicate functionとも呼ばれる)を定義することをしばしば望んでいる.言語自体が関数内部でクラスを定義できるため、関数オブジェクトの使用を考慮することができますが、これは通常、面倒で冗長であり、コードの流れを阻害します.また,標準C++では関数内部に定義されたクラスをテンプレートに使用することは許されないので,前述のやり方は不可能である.
C++11のlambdaへのサポートは上記の問題を解決することができる.
lambda関数は、次のように定義できます.
[](int x, int y) { return x + y; }
この無名関数の戻りタイプはdecltype(x+y)である.lambda関数が「return expression」の形式に合致する場合にのみ、その戻りタイプは無視されます.前述の場合、lambda関数は1つの文のみとすることができる.
より複雑な例では、戻りタイプは以下のように明確に指定できます(戻り値タイプは明示的に指定できます).
[](int x, int y) -> int { int z = x + y; return z + x; }
この例では、中間結果を格納するために一時的なパラメータzが作成される.一般的な関数と同様に、zの値は、次回この名前のない関数が再び呼び出されるまで保持されません(中間値は呼び出しの前後には存在しません).
lambda関数がvoidなどの戻り値を返さない場合、その戻りタイプは完全に無視され得る(戻り値を明示的に指定する必要はなく、
-> void
コードを書く必要はない).lambda関数と同じ役割ドメインを定義するパラメータ参照も使用できます.このようなパラメータセットは一般にclosure(閉パケット)と呼ばれる.
Lambda関数はlambda関数以外のautomatic storage durationを有する変数をキャプチャすることができる.関数体をこれらの変数の集合と合わせて閉パケットと呼ぶ.これらの外部変数は、lambda式を宣言するときに、四角カッコ
[
および]
に列挙される.空の四角カッコは、外部変数がcaptureされていないことを示します.これらの変数は、値伝達によって取得または参照によって取得されます.値が取得された変数の場合、デフォルトは読み取り専用です.これらの変数を変更すると、コンパイルエラーが発生します.しかし、lambda式のパラメータテーブルのカッコの後ろにmutableキーワードを使用すると、lambda関数内の文が伝値参照の変数を変更することができます.これらの変更はlambda式と(実際には関数オブジェクトで実装されています)同じライフサイクルがありますが、伝達値によって取得された外部変数の値には影響しません.lambda関数はstaticストレージ期間を持つ変数を直接使用できます.lambda関数の取得リストにstaticストレージ期間の変数が与えられている場合、コンパイル時に警告が表示され、lambda関数に従って直接これらの外部変数を使用して処理されます.このstaticストレージ期間を持つ変数は、トランスポートキャプチャとして宣言されても、変更は実際に外部変数を直接変更します.コンパイラはlambda関数に対応する関数オブジェクトを生成する場合、関数オブジェクトのデータメンバーで「キャプチャ」されたstaticストレージ期間の変数を保持しません.例:[] // 。 。
[x, &y] // x ( ),y 。
[&] // 。
[=] // 。
[&, x] // x 。 。
[=, &z] // z 。 。
closureは次のように定義され、使用されます.
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&total](int x) {
total += x;
});
std::cout << total;
上記の例では、someList要素の合計を計算し、印刷することができます.パラメータtotalはlambda関数closureの一部であり、述語関数に参照で渡されるため、その値はlambda関数によって変更することができる.
参照されたシンボル&を使用しない場合、代表パラメータはlambda関数に値を伝達するように入力されます.使用者は,この表現法を用いてパラメータ伝達の方法,すなわち伝達値,または伝達参照を明確に区別できる.lambda関数は、std::functionオブジェクトに配置するなど、宣言された場所でその場で使用できないため、この場合,パラメータがclosureに参照でリンクされている場合は,無意味で危険な行為である.
lambda関数が定義された役割ドメインでのみ使用される場合は、stackに参照されているすべてのパラメータを表すlambda関数を[&]で宣言できます.すべてのパラメータは、参照によって入力されます.明示的に指定する必要はありません.
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&](int x) {
total += x;
});
パラメータがlambda関数に伝達される方法は、実装によって変化する可能性があり、一般に、lambda関数がその役割ドメイン関数のstackポインタを保持し、これによって領域パラメータにアクセスすることが望ましい.
[&]ではなく[=]を使用すると、すべての参照を表すパラメータが値伝達で使用されます.
異なるパラメータの場合、値または参照は混和して使用できます.例えば、使用者は、すべてのパラメータを参照で使用することができますが、1つの値で使用されるパラメータがあります.
int total = 0;
int value = 5;
[&, value](int x) { total += (x * value); };
totalはlambda関数に参照を伝達する方式で伝達され、valueは伝達値である.
クラスの非静的メンバー関数で定義されたlambda式は、
this
ポインタを明示的または暗黙的にスナップすることができ、クラスオブジェクトのデータメンバーおよび関数メンバーを参照することができる.Lambda関数の関数体では、次の変数にアクセスできます.Lambda関数のデータ型は関数オブジェクトであり、保存時に
std::function
テンプレートタイプまたはauto
キーワード、またはauto
キーワードを使用する必要があります.例:#include
#include
#include
double eval(std::function <double(double)> f, double x = 2.0)
{
return f(x);
}
int main()
{
std::function<double(double)> f0 = [](double x){return 1;};
auto f1 = [](double x){return x;};
decltype(f0) fa[3] = {f0,f1,[](double x){return x*x;}};
std::vector<decltype(f0)> fv = {f0,f1};
fv.push_back ([](double x){return x*x;});
for(int i=0;i<fv.size();i++)
std::cout << fv[i](2.0) << std::endl;
for(int i=0;i<3;i++)
std::cout << fa[i](2.0) << std::endl;
for(auto &f : fv)
std::cout << f(2.0) << std::endl;
for(auto &f : fa)
std::cout << f(2.0) << std::endl;
std::cout << eval(f0) << std::endl;
std::cout << eval(f1) << std::endl;
std::cout << eval([](double x){return x*x;}) << std::endl;
return 0;
}
lambda関数のスナップ式が空の場合、通常の関数ポインタで格納または呼び出すことができます.例:
auto a_lambda_func = [](int x) { /*...*/ };
void (* func_ptr)(int) = a_lambda_func;
func_ptr(4); //calls the lambda.
lambda関数がクラスのメンバー関数に定義されている場合、クラスオブジェクトの参照を使用して、内部のメンバーにアクセスできます.
[](SomeType *typePtr) { typePtr->SomePrivateMemberFunction(); };
これはlambda関数が作成した役割ドメインがSomeTypeのメンバー関数の内部にある場合にのみ動作します.
メンバー関数でオブジェクトを指すthisポインタは、lambda関数に明示的に入力する必要があります.そうしないと、メンバー関数のlambda関数では、オブジェクトのパラメータまたは関数は使用できません.
[this]() { this->SomePrivateMemberFunction(); };
lambda関数が[&]または[=]の形式を使用する場合、thisはlambda関数で表示されます.
Lambda関数はコンパイラ依存型の関数オブジェクトです.このタイプの名前はコンパイラ自身でしか使用できません.使用者がlambda関数をパラメータとして入力する場合は、そのタイプがテンプレートタイプであるか、std::functionを作成してlambdaの値を取得する必要があります.autoキーワードを使用すると、lambda関数を格納できます.
auto myLambdaFunc = [this]() { this->SomePrivateMemberFunction(); };
auto myOnheapLambdaFunc = new auto([=] { /*...*/ });