ラムダ式を使ってみる
下準備が長かったのですが、ラムダ式を使ってみましょう。使ってみれば、こんな分かりやすいものはありません。
ラムダ式の定義
ラムダ式は、以下のように定義します。最初から細かくするとワケがわからなくなるので、最もシンプルな形を紹介します。
[キャプチャ句](パラメータリスト) -> 戻り値の型 { ラムダ式の本体 }
キャプチャって何?ということはひとまず置いておきまして(あとでイヤ~なテーマのところで出てきます)、パラメータリストとは引数です(パラメータ宣言子とも呼ばれます)。戻り値は分かりますね。ラムダ式の本体は関数の中身です。気になるのは、関数の名前がどこにもないというところでしょうか。と言いますか、キャプチャ句の部分を取って関数の名前を付ければ、普通の関数定義ですね。関数の名前がないので、ラムダ式は無名関数とか匿名関数とも呼ばれます。通常の関数と同様に、引数なしも戻り値なしの場合も記述できます。
簡単な例でラムダ式を見てみます。もちろん(!)、キャプチャ句は省いてます。
auto func = [](int x, int y) { return x * y; }; cout << func(100, 200) << endl;
整数型の引数xとyを受け取り、その積を返すという単純な関数を定義してfuncに入れています。この場合、戻り値の型を省略しているので、関数定義の戻り値から型推論されます。また、autoで受けることによりラムダ式の型の記述も省略できます。ここは、autoで受けることで型推論の恩恵を受けるのが良いでしょう。funcが、関数オブジェクトを所有することになります。なおC++ 14では、パラメータリストの型にautoを指定することで、ジェネリックな型を渡すことができます。
関数ポインタの話を思い出してください。ポインタに入った関数を呼び出すときの書式に悩みませんでしたか? ラムダ式では、関数オブジェクトの入った変数をそのまま記述することで、シンプルに関数を呼び出すことができます。
なお、ラムダ式には属性、例外仕様、mutableを指定できますが、ちょっと難しくなるので簡単に紹介するのみにします。なおmutableは、キャプチャ句と関連があるので、これは後回しにします。
情報を伝える属性
属性(attribute)とは、コンパイラにソースコードについての情報を伝える構文です。[[attribute]]のように記述します。JavaやC#にもある属性と似たような位置付けと思ってよいかも知れません。属性を指定することで、関数の動作や引数の制約についての情報をコンパイラに与えることができます。C++ 11で[[noreturn]]と[[carries_dependency]]が実装され、その後のC++でいくつかの属性が追加されました(それがどのようなものなのかは本稿とは関連が薄いので省略)。重要なのは、ラムダ式で使える属性です。C++ 23時点で、以下の3つが使えます。
- [[noreturn]]:関数から戻らないことを示す(C++ 11)
- [[nodiscard]]:関数の戻り値を必ず受け取る(C++ 17)
- [[deprecated]]:関数は非推奨であることを示す(C++ 14)
書いておいて何ですが、日常ではあまり意識しなくてもいいかもしれませんね(最近、この手の新機能ってやたら目につきませんか?)。
例外仕様
例外仕様(例外指定という場合もあります)とは、関数が何か問題を起こしたときに、どう対応しているか? というのをコンパイラに伝えるためのものです。そういう意味では属性と似ていますね。属性と違うのは、こちらは言語仕様だということです。C++ 11でnoexceptが導入されて、引数とともに以下のように使われます。
- noexcept、noexcept(true):関数は例外をスローしない
- noexcept(false):関数は任意の型の例外をスローする
Javaなんかのthrowsに似ているのかな? と少し思いました。コンパイラが最適化に使用したりするようです。
ここで、これらを含めたラムダ式の書き方として書式を再掲しておきます。
[キャプチャ句](パラメータリスト) mutable 属性 例外仕様 -> 戻り値の型 { ラムダ式の本体 }