第52回 ラムダ式
その場で作る無名関数。[キャプチャ](引数){本体} というコンパクトな構文で関数を書けます。STL アルゴリズム(std::sort、std::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 で昇順(既定)。