ラムダ関数を使用する - C++ プログラミング

PROGRAM


ラムダ関数を使用する

C++11 では、ラムダ関数という、匿名関数のようなものが使用できます。

[クロージャ](引数群)->戻り値の型{ 実装 }

いろいろ省略して簡単に書くなら、

[](引数群){ 実装 }

という風な程度でも書けます。

 

定義の仕方の要所としては、次のような感じになります。

  1. 引数群は、データ型と変数名をセットで、必要な数だけカンマ区切りで指定します。
  2. クロージャは、変数名だけを、必要な数だけカンマ区切りで指定します。変数を直接読み書きしたい場合は、変数名の頭に "&" を記載します。クロージャが不要な場合は [] だけを記載します。
  3. 戻り値の型は "->" と合わせて省略できます。戻り値を省略した場合は、実装内の return 文で返す値の型か、それがなければ void を指定したのと同じ意味になります。
  4. 実装は、関数をプログラムするのと同じように複数行のプログラムで記述できます。

このような要領でラムダ関数を作成して行きます。

 

クロージャ

クロージャというのは、ラムダ関数の外側で定義されている変数を、ラムダ関数内でも使用できるようにするためのものです。

ラムダ関数の引数との違いなど、クロージャの存在意義がまだ今ひとつ分らないのですけど、使い方としては、使いたい変数を使いたいだけ、クロージャのところにカンマ区切りで指定することで、ラムダ関数内でそれらを使用できるようになります。

&変数名 変数名の前に "&" をつけて指定すると、その変数をラムダ関数内でもそのまま読み書きできるようになります。引数の参照渡しと同じ使い方になります。
変数名 変数名を指定すると、その変数の値がコピーされた同じ名前の変数がラムダ関数内で使えるようになります。引数の値渡しと同じ使い方になります。
& "&" を指定すると、スコープ内の全ての変数は、ラムダ関数内でもそのまま読み書きできるようになります。これと併せて別の変数を指定した場合は、指定された変数には、指定された使い方が採用されます。
= "=" を指定すると、スコープ内の全ての変数は、ラムダ関数内でも、値がコピーされた同じ名前の変数として使えるようになります。これと併せて別の変数を指定した場合は、指定された変数には、指定された使い方が採用されます。
this ラムダ関数内でも、クラスインスタンスが自分自身を示すのに使う this ポインタを使いたい場合は、クロージャにそれを明記する必要があります。ただし "&" か "=" が指定されている場合は不要です。

ただし、クロージャーが指定されたラムダ関数は、それらの変数が生きているスコープ内で使わないと、実行時に例外エラーで落ちてしまうので注意が必要です。

 

ラムダ関数の使い方

こういったところを踏まえて、例えば this ポインタと、読み書き可能な int 型変数 sum をクロージャに渡す場合は、次のような書き方になります。

[this,&sum](int times){ while (times--) sum += this->value(); }

このようにすることで、クロージャーとして this と &sum がラムダ関数に渡されます。また、引数として int 型の times が渡されます。

このとき、クロージャのところに記載するのは変数名だけで、そのデータ型は記載しません。引数の方には変数名とデータ型を指定します。

 

これを実行するためには、std::for_each などの関数としてラムダ関数を直接指定するか、次のようにいったん std::function<T> 型の変数に代入して、関数ポインタのように呼び出す方法もあります。

このとき、std::function<T> が定義されている <functional> ヘッダーが #include されている必要があります。

// ラムダ関数のクロージャで使用する変数は、事前に定義されている必要があります。

int sum = 0;

 

// std::function<T> 型の変数に、ラムダ関数を代入しています。

std::function<void(int)> function = [this,&sum](int times){ while (times--) sum += this->value(); };

 

// std::function<T> 型の変数に引数を渡して、代入されている関数を実行します。

function(100);

std::function<T> の使い方としては、テンプレートの部分に "戻り値 (引数の型)" という形で記述します。引数が複数ある場合は、形名だけをカンマ区切りで指定します。

 

なお、C++11 には、データ型を初期化時に自動決定する auto というキーワードが用意されています。

これを使って次のように、簡単にラムダ関数を代入することも可能です。

// auto キーワードを指定した変数に、ラムダ関数を簡単に代入することもできます。

auto function = [this,&sum](int times){ while (times--) sum += this->value(); };

呼び出し方も std::function<T> を使ったときと同じなので、ちょっとしたラムダ関数を変数に定義してそれを実行したいようなときには、こちらの方が重宝するかもしれません。


[ もどる ]