ラムダ式

日λ…


auto

auto はこれから学ぶのに必要な知識なので、一緒に解説しておく。

auto は、少し特殊な型だよ。変数ではその初期値から、その型を推論するんだ。

auto number = 0; // int 型
auto bigger = 0L; // long 型
auto wonderful = 0LL; // long long 型

auto single = 1.2f; // float 型
auto doppio = 2.4; // double 型

auto text = std::string("Hey!"); // std::string 型

戻り値型では、戻り値の型から推論される。引数の型には使えない (そのうち使えるようになるかも)。

auto add(int a, int b) { // int add(int, int) になる
  return a + b; // int + int は int だからね
}

auto sub(int a, int b) -> int { // auto と書いても、-> の後に明示できる
  return a - b;
}

長ったらしいイテレータクラスには、めちゃくちゃ有効。

std::vector<int> data;

std::vector<int>::iterator head = data.begin(); // 長い
auto tail = data.end(); // 短い

ラムダ式

いままでの関数は main 関数の外にぽつぽつと書いてた。

ラムダ式を使うと、main 関数といった 関数の中ででも関数が作れる

文法はこう。

[] { 0 個以上の文 }

#include <iostream>

int main() {
  [] {
    std::cout << "Hello!\n";
  };
}

呼び出し

これは関数を作っただけで特に実行していない。

普通の関数と同じように、後ろに () をつけて呼び出せば実行できる。

#include <iostream>

int main() {
  [] {
    std::cout << "Hello!\n";
  }();
}

変数への代入

ラムダ式はれっきとした であり、変数に代入できちゃう。特定の型はないけれど、auto 型変数は使える。

auto func = [] {
  std::cout << "wow wow! ";
};
func();
func();

引数を受け取る

ラムダ式で引数を受け取るときは、[]{} の間に、普通の関数と同じように引数リストを書く。

[] ( 引数リスト ) { 0 個以上の文 }

auto show = [] (int x) {
  std::cout << "X is " << x << "\n";
};
show(2);
show(40);

引数がないときは、引数リストの () は省略できるってだけ。その方が見やすいのかな?

値を戻す

ラムダ式で戻り値を作るときでも、特に型の指定は要らない。

auto add = [] (int a, int b) { return a + b; };
add(2, 4);
add(3, add(5, 8));

引数リストの ) の後ろに -> 型 って書いて明示することもできる。

auto sub = [] (int a, int b) -> int { return a - b; }
sub(4, 2);
sub(sub(8, 5), 1);

ラムダキャプチャ

ラムダ式が書いてあるスコープと同じところの変数にアクセスする場合は、

ラムダキャプチャ を書くことで、使用できるようになる。

ラムダキャプチャを書くのは、[] の中だよ。そのためにあるからね。

コピーキャプチャ

[] の中に変数名をそのまま書くと、

その変数の数値をコピーして ラムダ式で作った関数が値を保持する

auto adder(int a) { // 関数を返す関数
  return [a] (int b) {
    return a + b;
  };
  // 関数の処理が終わると引数の a はもう使えなくなるはず
}

int main() {
  auto add3 = adder(3);
  // add3 は、a の値をコピーした 3 と引数を足して返す
  add3(2); // 5
  add3(4); // 7
  auto add10 = adder(10);
  add10(4); // 14
}

参照キャプチャ

コピーキャプチャの文法で、変数名の前に & をつけると参照キャプチャになる。

コピーキャプチャとは違って変数の値を変えられる。

int count = 0;
auto counter = [&count] { ++count; };
auto show = [count] { std::cout << "Count: " << count << "\n"; }
counter();
counter();
counter();
show();

ただし、使えなくなっているはずの変数にもアクセスできてしまう。挙動は不定になる。

#include <iostream>

auto lazy_presenter(int a) { // 関数を返す関数
  return [&a] () {
    std::cout << a << "\n"; // おかしな動作をするかもしれない
  };
  // 関数の処理が終わると引数の a はもう使えなくなるはず
}

int main() {
  auto presenter = lazy_presenter(2);
  presenter();
}

ラムダキャプチャは他にも機能があるけれど、それはまたの機会に。