[Work/Class/C++11の場合/CPPBasic]

C++11の場合 - ラムダ式

ラムダ式

ラムダ式(lambda expression)とは,関数オブジェクトをその場で定義して使用することである.

C++の思想はJavaと同じく「様々なクラスを綿密に設計した上で,そのクラスを用いて処理を作る」ものだったが,JavaScript,Python,RubyなどのLL系言語が幅広く使われるようになった結果,「クラス以外の関数オブジェクトをその場で記述して使う」思想がC++にも輸入されるようになった.

auto 関数オブジェクトを保持する変数名 = [](引数){処理内容};

という書式でラムダ式は定義される.大かっこ[]が「この先はラムダ式の定義である」を意味し,その後の小かっこ(引数)で関数の引数を決め,中かっこ{処理内容}に実際の処理内容を書く.

この式が返す「関数オブジェクト」を,auto型指定した変数に突っ込み,実行することができる.

auto result = 関数オブジェクトを保持する変数名(引数);

一度定義してしまえば,通常の関数と同じように使うことができる.

変数のキャプチャ

大かっこ[]の中身に,&=を入れることで,ラムダ式の外側にある変数を,ラムダ式で定義した関数の中で使うことができる.これを「キャプチャ」と呼び,C++のラムダ式の特徴になっている.

参照キャプチャ

int x=2, y=3; // 変数の定義
auto func1 = [&](){ return x + y;}; // 「参照キャプチャ」ラムダ式の定義
x = 4; // 変数の中身の変更
y = 2;
auto result = func(); // 変数の中身の変更が適用され,結果は6を返す

大かっこの中に&を入れると「参照キャプチャ」となり,ラムダ式の外側での変数に対する変更がそのまま適用される.(Javaで関数の引数としてインスタンスを撮る時と同じ)

コピーキャプチャ

int x=2, y=3; // 変数の定義
auto func1 = [=](){ return x + y;}; // 「コピーキャプチャ」ラムダ式の定義
x = 4; // 変数の中身の変更
y = 2;
auto result = func(); // 定義時のxとyが入っているので,変数の中身の変更は適用されず,結果は5を返す

大かっこの中に=を入れると「コピーキャプチャ」となり,ラムダ式の定義時の変数の状態がコピーされ,関数オブジェクト内で保存される.そのため定義時から実行時までに変数の中身を入れ替えたとしても,実行時には定義時の値が使われる.

インスタンスの参照キャプチャ

インスタンスのメンバ関数の中でラムダ式を使う場合,大かっこの中にthisを入れると,インスタンスのメンバ変数やメンバ変数を全てラムダ式の中で使うことができる.これは参照である.

関数オブジェクトを引数に取る関数

Pythonでちょこっとだけ触れたが「関数を引数に取る関数」というものが存在する.これを高階関数と呼ぶが,この引数である関数にラムダ式を渡すことができる.

C++11で採用されている「標準アルゴリズムライブラリ」std::algorithmに属する関数群には,先に出てきた標準可変長配列std::vectorなどを操作するための高階関数が含まれているため,ラムダ式が必要とされるケースがある.std::algorithmはSTL Collection編で触れる.

コード例

#include <iostream>

int main(){
  // 関数オブジェクトを,一度auto型の変数に突っ込んでから,使用する.
  auto plus = [](int a, int b){
    return a + b;
  };
  auto result = plus(2, 3);
  std::cout << "Function returns: " << result << std::endl;


  // Lambdaの外で定義した変数をLambdaの関数定義の中で使う
  // キャプチャと呼ぶ
  int x = 3;
  int y = 4;
  auto ref_plus = [&](){ // & は「参照する」キャプチャ
    return x + y;
  };
  result = ref_plus();
  std::cout << "Function returns: " << result << std::endl;


  auto copy_plus = [=](){ // =は,ラムダ式定義時の値を「コピーする」キャプチャ
    return x + y;
  };

  // 外の変数の中身を変更してから「参照キャプチャ」のラムダ関数オブジェクトを実行すると,
  // 「参照」なので,定義時と結果が変わる
  x = 6;
  y = 8;
  result = ref_plus();
  std::cout << "Reference Capture returns: " << result << std::endl;
  
  // 「コピーキャプチャ」のラムダ式は,
  // 定義した時点での値が関数オブジェクトの中で保存され,使われる
  result = copy_plus();
  std::cout << "Copy Capture  returns: " << result << std::endl;

  return 0;
}