実は以下の形の関数は、「関数」ではない。
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
}
ラムダ式についてはまだいろいろな機能があるが、本書での解説はここまでとする。