# ラムダ式
実は以下の形の関数は、「関数」ではない。
auto function = []( auto value ) { return value } ;
これはラムダ式と呼ばれるC++の機能で、関数のように振る舞うオブジェクトを作るための式だ。
# 基本
ラムダ式の基本の文法は以下のとおり。
[](){} ;
これを細かく分解すると以下のようになる。
[] // ラムダ導入子
() // 引数リスト
{} // 複合文
; // 文末
ラムダ導入子はさておく。
引数リストは通常の関数と同じように型名と名前を書ける。
void f( int x, double d ) { }
[]( int x, double d ) { } ;
ラムダ式では、引数リストにautoキーワードが使える。
[]( auto x ) { } ;
このように書くとどんな型でも受け取れるようになる。
int main()
{
auto f = []( auto x )
{ std::cout << x ; } ;
f(0) ; // int
f(1.0) ; // double
f("hello"s) ; // std::string
}
複合文は{}だ。この{}の中に通常の関数と同じように複数の文を書くことができる。
[]()
{
std::cout << "hello"s ;
int x = 1 + 1 ;
} ;
最後の文末は文の最後に付けるセミコロンだ。これは"1+1 ;"とするのと変わらない。"1+1"や"[](){}"は式で、文は式を使うことができる。式だけが入った文を専門用語では式文と呼ぶが特に覚える必要はない。
1 + 1 ; // OK、式文
[](){} ; // OK、式文
ラムダ式は式なので式文の中に書くことができる。
ラムダ式は式なので、そのまま関数呼び出しすることもできる。
void f( std::string x )
{
std::cout << x ;
}
int main()
{
f( "hello"s ) ;
[]( auto x ){ std::cout << x ; }( "hello"s ) ;
}
これはわかりやすくインデントすると以下のようになる。
f // 関数
( "hello"s ) ; // 関数呼び出し
// ラムダ式
[]( auto x ){ std::cout << x ; }
( "hello"s ) ; // 関数呼び出し
ラムダ式が引数を1つも取らない場合、引数リストは省略できる。
// 引数を取らないラムダ式
[](){} ;
// 引数リストは省略できる
[]{} ;
ラムダ式の戻り値の型はreturn文から推定される。
// int
[]{ return 0 ; } ;
// double
[]{ return 0.0 ; } ;
// std::string
[]{ return "hello"s ; } ;
return文で複数の型を返した場合は推定ができないのでエラーになる。
[]( bool b )
{
if ( b )
return 0 ;
else
return 0.0 ;
} ;
戻り値の型を指定したい場合は引数リストのあとに->を書き、型名を書く。
[]( bool b ) -> int
{
if ( b )
return 0 ;
else
// doubleからintへの変換
return 0.0 ;
} ;
戻り値の型の推定は通常の関数も同じだ。
// int
auto f() { return 0 ; }
// 戻り値の型の明示的な指定
auto f() -> int { return 0 ; }
# キャプチャー
ラムダ式は書かれている関数のローカル変数を使うことができる。これをキャプチャーという。キャプチャーは通常の関数にはできないラムダ式の機能だ。
void f()
{
// ローカル関数
auto message = "hello"s ;
[=](){ std::cout << message ; } ;
}
キャプチャーにはコピーキャプチャーとリファレンスキャプチャーがある。
# コピーキャプチャー
コピーキャプチャーは変数をコピーによってキャプチャーする。
コピーキャプチャーをするには、ラムダ式を[=]と書く。
int main()
{
int x = 0 ;
// コピーキャプチャー
[=]{ return x ; } ;
}
コピーキャプチャーした変数はラムダ式の中で変更できない。
int main()
{
int x = 0 ;
// エラー
[=]{ x = 0 ; } ;
}
変更できるようにする方法もあるのだが、通常は使われない。
# リファレンスキャプチャー
リファレンスキャプチャーは変数をリファレンスによってキャプチャーする。
リファレンスを覚えているだろうか。リファレンスは初期化時の元の変数を参照する変数だ。
int main()
{
int x = 0 ;
// 通常の変数
int y = x ;
// 変数を変更
y = 1 ;
// xの値は変わらない
// リファレンス
int & ref = x ;
// リファレンスを変更
ref = 1 ;
// xの値が変わる
}
リファレンスキャプチャーを使うには、ラムダ式を[&]と書く。
int main()
{
int x = 0 ;
[&] { return x ; } ;
}
リファレンスキャプチャーした変数をラムダ式の中で変更すると、元の変数が変更される。
int main()
{
int x = 0 ;
auto f = [&]{ ++x ; } ;
f() ; // x == 1
f() ; // x == 2
f() ; // x == 3
}
ラムダ式についてはまだいろいろな機能があるが、本書での解説はここまでとする。