C++ Learning

第52回 ラムダ式

その場で作る無名関数[キャプチャ](引数){本体} というコンパクトな構文で関数を書けます。STL アルゴリズム(std::sortstd::find_if)と組み合わせると、処理の流れを滑らかに 1 行で書けるのがモダン C++ の威力。

このページで押さえること
✅ 最低限ここだけ覚える
  • [](int x){ return x*2; } の形
  • [] = キャプチャ、() = 引数、{} = 本体
  • auto f = [](...){...}; f(args); で名前付け可
  • STL の比較・述語関数の場所にそのまま渡せる
⭐ 余裕があれば読む
  • 値キャプチャ [x] vs 参照キャプチャ [&x]
  • [=] / [&] のデフォルトキャプチャ
  • ジェネリックラムダ(auto 引数)
  • mutable / 戻り値指定

1. まず触ってみる

first_lambda.cpp最小例
// その場で関数を作る auto square = [](int x) { return x * x; }; std::cout << square(5); // 25 // STL アルゴリズムと組み合わせ std::vector<int> v = {1, 2, 3, 4, 5}; std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // 降順 auto it = std::find_if(v.begin(), v.end(), [](int x) { return x > 3; }); // 3 より大
▶ フィルタ/変換デモ
v = {1,2,3,4,5,6,7,8,9,10}

2. 構文の要素

完全構文詳細
[capture](params) -> return_type { body } ^^^^^^^ ^^^^^^ ^^^^^^^^^^^ ^^^^ キャプチャ 引数 戻り値型 本体 (外の変数を (普通の (省略可) 取り込む) 関数と同じ)

省略形

最小形最短
// キャプチャなし、引数なし、戻り値なし []{ std::cout << "hi\n"; }; // 戻り値型は推論される [](int x) { return x * 2; };
明示形フル
[](int x) -> double { return x / 2.0; }; // 複雑な戻り値型を指定したいとき

3. キャプチャ ― 外の変数を取り込む

値キャプチャ [x]コピー
int n = 10; auto f = [n](int x) { return x + n; }; f(5); // 15 n = 100; // ラムダ内部の n は変わらない f(5); // 15 (コピーした値を使う)
参照キャプチャ [&x]共有
int n = 10; auto f = [&n](int x) { return x + n; }; f(5); // 15 n = 100; // 外の変化が反映される f(5); // 105

デフォルトキャプチャ

[=]全て値
int a = 1, b = 2; auto f = [=] { return a + b; }; // a, b がコピーで取り込まれる
[&]全て参照
int a = 1, b = 2; auto f = [&] { return a + b; }; // a, b が参照で取り込まれる
注意: [&] を使うラムダを外のスコープを超えて生きさせると、参照先が消えてダングリング。コールバック保存などには[=] か明示キャプチャを使うのが安全。

4. STL アルゴリズムとの相性

一気に書ける流麗
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6}; // 条件に合う個数 auto cnt = std::count_if(v.begin(), v.end(), [](int x) { return x > 3; }); // 条件で削除 v.erase( std::remove_if(v.begin(), v.end(), [](int x) { return x < 3; }), v.end()); // 各要素を変換 std::transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 10; });

次章以降で、これらの STL アルゴリズムを分野別に詳しく扱います。

ここまででラムダは OK
応用的な機能は必要になったとき調べれば大丈夫。

5. 応用的な機能

ジェネリックラムダ(C++14)

auto 引数C++14~
auto pr = [](auto a, auto b) { return a + b; }; pr(1, 2); // int pr(1.5, 2.5); // double pr(std::string{"hi "}, "world"); // string // 関数テンプレートと同じ動作

mutable ― 値キャプチャを内部で変更

mutable内部状態
auto counter = [n = 0]() mutable { return ++n; }; counter(); // 1 counter(); // 2 counter(); // 3 // 値キャプチャした n を内部で書き換える
広告スペース

確認クイズ

ラムダを 4 問で確認。

Q1. ラムダ式の構文 [] は何を表す?

戻り値
引数リスト
キャプチャリスト(外の変数を取り込む指定)
関数名
[] はキャプチャ節。[n] で値コピー、[&n] で参照を取り込み。

Q2. 次の 2 つの違いは?
A: [n](){ return n; }
B: [&n](){ return n; }

A は参照、B は値コピー
A は値コピー、B は参照
同じ意味
B はコンパイルエラー
[n] = 値コピー、[&n] = 参照。値コピーはラムダを作った瞬間の値を保持、参照は外の変化が反映されます(ただしダングリングに注意)。

Q3. 戻り値の型が自動推論されないケースはどれ?

常に推論される
複雑な型のとき明示必須
return 文が複数あり戻り型が異なるとき
引数が 2 つ以上のとき
return 文が複数あって返す型が一貫しないと推論に失敗します。そのときは -> Type で明示。

Q4. STL の std::sort で降順に並べるラムダは?

[](int a, int b){ return a < b; }
[](int a, int b){ return a > b; }
[](int a, int b){ return -a + b; }
[](int a){ return -a; }
「a が先に並ぶか」を true/false で返す関数。a > b で降順、a < b で昇順(既定)。
← 前の講座
第51回 確認問題