コンテンツにスキップ

C++/ラムダ式

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++

はじめに

[編集]

ラムダ式の概要

[編集]

ラムダ式(または無名関数)は、C++11から導入された機能であり、関数を定義するための簡潔で柔軟な方法を提供します。ラムダ式は、関数オブジェクト(関数ファンクタ)をインラインで定義し、名前を持たずに一時的な用途で使用することができます。これはコードの簡潔さと可読性を向上させます。

ラムダ式の重要性と役割

[編集]

ラムダ式は、C++プログラムでコンパクトで直感的なコードを記述するための強力な手段です。STLアルゴリズムやライブラリでコールバック関数として使用される場面においても、ラムダ式を用いることでコードの冗長さを排除し、明確で保守性の高いコードを実現できます。

ラムダ式の基礎

[編集]

ラムダ式とは何か?

[編集]

ラムダ式は、関数オブジェクトをインラインで定義するための構文です。名前を持たずに使い捨て的に使用されるため、簡潔な関数の記述に最適です。

従来の関数との比較

[編集]

従来の関数は名前を持ち、再利用が容易ですが、ラムダ式は匿名関数として短期間の使用を意図しています。関数ポインタは従来の関数に限定されますが、ラムダ式は関数オブジェクトとして自動的に扱われ、より多用途です。

ラムダ式の基本構文と使い方

[編集]

ラムダ式は、[](キャプチャリスト)、()(引数リスト)、{}(関数本体)で構成されます。例えば:

[]() { /* 関数本体 */ }

ラムダ式は変数に代入したり、関数の引数として使用できます。キャプチャリストにより外部変数を使用することも可能です。

キャプチャ

[編集]

キャプチャとは何か?

[編集]

キャプチャは、ラムダ式内で外部変数にアクセスするための仕組みです。これにより、ラムダ式は外部のスコープの変数を操作できます。

ラムダ式での変数のキャプチャ方法

[編集]

キャプチャリストに変数を指定することで、外部変数をラムダ式内に取り込むことができます。

キャプチャリストの構文と利用法

[編集]

キャプチャリストの構文は以下の通りです:

  • []: キャプチャなし。外部変数へのアクセス不可。
  • [&]: すべての外部変数を参照キャプチャ。
  • [=]: すべての外部変数を値キャプチャ。
  • [x, &y]: xを値キャプチャ、yを参照キャプチャ。
int x = 5;
auto lambda = [x]() { std::cout << x; };
  • [変数 = 式]: 初期化キャプチャで特定の式の値をラムダ式に持ち込みます。
int a{5};
auto lambda = [v = a + 1]() {
    std::cout << v << std::endl;
};

ラムダ式の呼び出し

[編集]

ラムダ式の呼び出し方法

[編集]

ラムダ式は関数オブジェクトとして扱われ、()を使って呼び出せます。

auto lambda = [](int x, int y) { return x + y; };
int result = lambda(3, 4); // ラムダ式の呼び出し

ラムダ式の引数の受け渡し

[編集]

引数は()内で定義され、呼び出し時に指定します。

auto lambda = [](int x, int y) { return x + y; };
int result = lambda(3, 4); // 引数として3と4を渡す

引数と戻り値の型推論

[編集]

型はコンパイラによって推論されます。明示的な型指定がない場合でも、自動的に型を判別します。

auto lambda = [](int x, int y) { return x + y; };

この場合、戻り値もintと推論されます。

スコープとライフタイム

[編集]

ラムダ式のスコープとライフタイム

[編集]

ラムダ式のスコープは定義された場所に依存します。変数をキャプチャしている場合、変数が有効な間にラムダ式を実行しないと未定義動作が生じる可能性があります。

#include <iostream>

void exampleFunction() {
    int localVar = 10;
    auto lambda = [&localVar]() {
        std::cout << "Local variable inside lambda: " << localVar << std::endl;
    };
    lambda();
}

int main() {
    exampleFunction();
    return 0;
}

ラムダ式のキャプチャ変数の有効範囲

[編集]

キャプチャした変数は、ラムダ式の定義されたスコープ内で有効です。

ラムダ式と関数オブジェクトの比較

[編集]

関数オブジェクトは通常、クラスとして明示的に定義されますが、ラムダ式はその場で簡潔に記述できます。

構造化束縛とラムダ式

[編集]

構造化束縛とは何か?

[編集]

C++17で導入された構造化束縛により、タプルやペアの要素を一度に複数の変数へ分割して代入できます。

ラムダ式での構造化束縛の使用方法

[編集]

ラムダ式内でも構造化束縛を使って引数を分割できます。

auto print_pair = [](auto&& pair) {
    auto [first, second] = pair;
    std::cout << "First: " << first << ", Second: " << second << std::endl;
};

std::pair<int, std::string> my_pair = {42, "hello"};
print_pair(my_pair); // Output: First: 42, Second: hello

この例では、ラムダ式の中で構造化束縛を使用して std::pair の内容を個別にアクセスしています。構造化束縛を使うことで、コードが簡潔になり、変数にアクセスするための分割や解釈が容易になります。

ラムダ式の進化と新機能

[編集]

C++14でのラムダ式の改良

[編集]

C++14では、ラムダ式にいくつかの改良が加えられました。特に注目すべき点は、ラムダ式の引数型の自動推論です。C++11では、引数の型を明示的に指定する必要がありましたが、C++14では auto キーワードを使用して型を推論できます。

auto add = [](auto x, auto y) {
    return x + y;
};

この例では、引数の型を auto と指定することで、任意の型の引数をラムダ式で受け取ることができます。これにより、テンプレート関数のような汎用性を持つラムダ式を記述できます。

C++17でのキャプチャ初期化

[編集]

C++17では、ラムダ式にキャプチャ初期化機能が追加されました。キャプチャリスト内で変数の初期化を行うことができ、外部スコープの変数をそのままキャプチャするのではなく、式を使って値を計算しながらキャプチャできます。

int a = 3;
auto lambda = [b = a * 2]() {
    std::cout << "b: " << b << std::endl;
};

この例では、a * 2 の結果を b にキャプチャして、ラムダ式内で使用しています。このキャプチャ方法は、ラムダ式内で安全に変数の初期化が行えるため、柔軟性が増します。

C++20での新機能

[編集]

C++20では、ラムダ式にさらなる改良が加えられました。特に、コンセプトのサポートが追加されたことで、ラムダ式の引数に型制約を設けることが可能になりました。

auto add_numbers = [](std::integral auto x, std::integral auto y) {
    return x + y;
};

この例では、std::integral を使用して、ラムダ式の引数が整数型であることを制約しています。これにより、ラムダ式が受け取る引数に型の制限を設け、コードの安全性と可読性が向上します。

まとめ

ラムダ式は、C++のプログラミングにおいて非常に強力なツールです。関数オブジェクトを簡潔に記述し、コードの可読性や保守性を高めることができます。C++のバージョンが進むにつれ、ラムダ式も進化し、より柔軟で効率的なコードを書くことが可能になっています。