🖥️

【C++言語入門】 第24回 ラムダ式の代入とauto

2025/03/28に公開

https://youtu.be/0ANrdOZ3gNE

四国めたん
\textcolor{pink}{四国めたん: }教師役ですわ

ずんだもん
\textcolor{lime}{ずんだもん: }生徒役なのだ

\footnotesize \textcolor{pink}{四国めたん:} こんにちは。四国めたんです

\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回は関数や ラムダ式 を変数に代入する方法についてお話しをしていきますわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数や ラムダ式 を変数に代入できるのか?

\footnotesize \textcolor{pink}{四国めたん:} 可能ですわね

\footnotesize \textcolor{lime}{ずんだもん:} なにかメリットはあるのか?

\footnotesize \textcolor{pink}{四国めたん:} ラムダ式 の場合は、いちど定義すれば再利用できるようになりますわ

\footnotesize \textcolor{lime}{ずんだもん:} それは便利なのだ

ラムダ式を再利用しよう

\footnotesize \textcolor{pink}{四国めたん:} ラムダ式 無名関数 のひとつですわ

\footnotesize \textcolor{lime}{ずんだもん:} 読んで字のごとく、 ラムダ式 には区別するための名前がないのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、基本的には、定義と同時に実引数を指定して使うことしかできませんでしたわ

\footnotesize \textcolor{lime}{ずんだもん:} そうだったのだ

\footnotesize \textcolor{pink}{四国めたん:} ただ、それでは使い勝手が悪く、非効率だと思いませんか?

\footnotesize \textcolor{lime}{ずんだもん:} たしかにその通りなのだ

\footnotesize \textcolor{lime}{ずんだもん:} いちど定義した ラムダ式 を再利用することはできないのか?

\footnotesize \textcolor{pink}{四国めたん:} 実は ラムダ式 を変数に代入することで、再利用することができるようになりますわ

\footnotesize \textcolor{lime}{ずんだもん:} その辺りを詳しく教えて欲しいのだ

関数を変数に代入しよう

\footnotesize \textcolor{pink}{四国めたん:} まず、C++言語だけではなく、C言語でも同じですが、あらゆる関数を変数に代入することが可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} ラムダ式 に限らないのか...

\footnotesize \textcolor{pink}{四国めたん:} はい、ただ変数の宣言は少々特殊ですわ

\footnotesize \textcolor{lime}{ずんだもん:} どのような形式になるのだ?

\footnotesize \textcolor{pink}{四国めたん:} 形式はこのようになりますわ

戻り値の型 (*変数名)(引数...);

\footnotesize \textcolor{pink}{四国めたん:} まず、最初は関数の 戻り値の型 を指定しますわ

\footnotesize \textcolor{lime}{ずんだもん:} voidでも指定する必要があるのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、省略はできませんわね

\footnotesize \textcolor{pink}{四国めたん:} 次に、括弧"()"内にポインタ"*"に続いて変数名を指定しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 変数名を括弧"()"で括るのは特徴的なのだ

\footnotesize \textcolor{pink}{四国めたん:} それに続いて関数と同じように引数を指定しますわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、引数名は省略してもOKですわ

\footnotesize \textcolor{lime}{ずんだもん:} 関数の宣言と同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} 実際の関数の指定は、変数の宣言と同時におこなうこともできますし、後で代入"="で指定することもできますわ

戻り値の型 (*変数名)(引数...) = 関数名;
変数名 = 関数名;

\footnotesize \textcolor{lime}{ずんだもん:} ふむ、同じ変数に、状況に応じた関数を割り当てることができるというわけだ

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} そして、実際に変数を用いて関数を呼び出すためには、このようにしますわ

変数名(実引数...);

\footnotesize \textcolor{lime}{ずんだもん:} 関数名の代わりに変数名を使う以外は、通常の関数の呼び出しと同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{pink}{四国めたん:} それでは足し算の関数を使ったプログラムを見てみましょう

#include <iostream>

int Add(int a, int b) {
  std::cout << "Addが呼ばれました" << std::endl;
  int c = a + b;
  return c;
}

int Add2(int a, int b) {
  std::cout << "Add2が呼ばれました" << std::endl;
  int c = (a + b) * 2;
  return c;
}

int main(int argc, char* argv[]) {
  int (*a)(int, int) = Add;
  std::cout << a(1, 2) << std::endl;
  a = Add2;
  std::cout << a(1, 2) << std::endl;
  return 0;
}

関数を変数に代入

\footnotesize \textcolor{lime}{ずんだもん:} 同じa(1, 2)と云う呼び出しであっても、変数"a"に代入した関数によって、呼び出される関数が変わっていることが判るのだ

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{lime}{ずんだもん:} 通常の関数の代わりに ラムダ式 を代入することも可能なのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、実は ラムダ式 も通常の関数と同様に代入することは可能ですわ

#include <iostream>

int main(int argc, char* argv[]) {
  int x = 1;
  int y = 2;
  int (*a)(int, int) = [](int a, int b) -> int { return a + b; };
  std::cout << a(x, y) << std::endl;
  return 0;
}

ラムダ式を変数に代入

\footnotesize \textcolor{lime}{ずんだもん:} お~、これで変数"a"を使って ラムダ式 を繰り返し使うことができるのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、ただし ラムダ式 をこのような変数に代入できるのは、 キャプチャリスト が空の場合のみに限られますわ

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} ためしに キャプチャリスト を使ってみましょう

#include <iostream>

int main(int argc, char* argv[]) {
  int x = 1;
  int y = 2;
  int (*a)(int, int) = [=]() -> int { return x + y; };
  std::cout << a(x, y) << std::endl;
  return 0;
}

ラムダ式のエラー

\footnotesize \textcolor{lime}{ずんだもん:} "lambda ->int" から "int (*)(int, int)" への適切な変換関数が存在しません というエラーが出ているのだ

\footnotesize \textcolor{pink}{四国めたん:} このように キャプチャリスト を使うと、通常の関数を変数に代入する場合の形式は使えなくなりますわ

std::functionを使いましょう

\footnotesize \textcolor{lime}{ずんだもん:} キャプチャリスト がある場合は、 ラムダ式 を代入する変数の型の指定はどうするのだ?

\footnotesize \textcolor{pink}{四国めたん:} さいわい、C++言語の標準テンプレートライブラリ(STL)には、関数を代入する変数のためのクラスが提供されていますわ

\footnotesize \textcolor{lime}{ずんだもん:} お~、すばらしいのだ

\footnotesize \textcolor{pink}{四国めたん:} それがstd::function<>ですわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} なおstd::function<>を使うためには、ヘッダーファイル"functional"をインクルードする必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} 使い方はこの通りですわ

std::function<戻り値の型(引数...)> 変数名;

\footnotesize \textcolor{lime}{ずんだもん:} 使えるのは ラムダ式 のみなのか?

\footnotesize \textcolor{pink}{四国めたん:} std::function<>は、通常の関数でも ラムダ式 でも使うことが可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} 結構、便利なのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに、山括弧"<>"を使っていることから判る通り、std::function<>はテンプレートクラスですわ

\footnotesize \textcolor{lime}{ずんだもん:} テンプレート引数には何を指定するのだ?

\footnotesize \textcolor{pink}{四国めたん:} テンプレート引数には、関数もしくは ラムダ式 の戻り値と引数を指定しますわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、引数名は省略してもOKですわ

\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ

\footnotesize \textcolor{pink}{四国めたん:} 実際の関数や ラムダ式 の指定は、変数の宣言と同時におこなうこともできますし、後で代入"="で指定することもできますわ

std::function<戻り値の型(引数...)> 変数名 = 関数名 or ラムダ式;
変数名 = 関数名 or ラムダ式;

\footnotesize \textcolor{lime}{ずんだもん:} ふむ、同じ変数に、状況に応じた関数や ラムダ式 を割り当てることができるというわけだ

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} そして、実際に変数を用いて関数や ラムダ式 を呼び出すためには、このようにしますわ

変数名(実引数...);

\footnotesize \textcolor{lime}{ずんだもん:} 関数名や ラムダ式 の代わりに変数名を使う以外は、通常の関数の呼び出しと同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{pink}{四国めたん:} それでは足し算の関数と ラムダ式 を使ったプログラムを見てみましょう

#include <iostream>
#include <functional>

int Add(int a, int b) {
  std::cout << "Addが呼ばれました" << std::endl;
  int c = a + b;
  return c;
}

int main(int argc, char* argv[]) {
  int x = 1;
  int y = 2;
  std::function<int(int, int)> a = Add;
  std::cout << a(x, y) << std::endl;
  std::function<int()> b = [=]() -> int {
    std::cout << "ラムダ式が呼ばれました" << std::endl;
    return x + y;
  };
  std::cout << b() << std::endl;
  return 0;
}

std::function<>

\footnotesize \textcolor{lime}{ずんだもん:} 変数"a"には関数Addを代入しているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、引数にはint型を2つ、戻り値にはint型を指定していますわ

\footnotesize \textcolor{lime}{ずんだもん:} 変数"b"には キャプチャリストコピーキャプチャ を指定した、int型の戻り値の ラムダ式 を指定しているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、Add関数も ラムダ式 も変数を通して呼び出すことができていますわね

autoキーワード

\footnotesize \textcolor{lime}{ずんだもん:} std::function<>クラスを使わずに キャプチャリスト を持つ ラムダ式 を変数に割り当てることはできないのか?

\footnotesize \textcolor{pink}{四国めたん:} 可能ではあるのですが、非常に複雑で、全く実用的ではありませんわ

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、ですので、通常はstd::function<>クラスを使ってくださいね

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} ところで、最近のC++言語では、autoキーワードによって型の推論ができるようになりましたわ

\footnotesize \textcolor{lime}{ずんだもん:} 型の推論?

\footnotesize \textcolor{pink}{四国めたん:} はい、初期値などからコンパイラが型を推論して割り当ててくれる機能が追加されたのですわ

\footnotesize \textcolor{lime}{ずんだもん:} お~、それは便利なのだ

\footnotesize \textcolor{pink}{四国めたん:} そして、変数の宣言の型指定の代わりにautoを使うことで、コンパイラが型を推論してくれるのですわ

\footnotesize \textcolor{lime}{ずんだもん:} 具体的な使い方を教えて欲しいのだ

\footnotesize \textcolor{pink}{四国めたん:} とりあえずプログラムを見てみましょう

#include <iostream>

int Add(int a, int b) {
  std::cout << "Addが呼ばれました" << std::endl;
  auto c = a + b;
  return c;
}

int main(int argc, char* argv[]) {
  auto x = 1;
  auto y = 2;
  auto a = Add;
  std::cout << a(x, y) << std::endl;
  auto b = [=]() -> int {
    std::cout << "ラムダ式が呼ばれました" << std::endl;
    return x + y;
  };
  std::cout << b() << std::endl;
  return 0;
}

auto

\footnotesize \textcolor{lime}{ずんだもん:} std::function<>の部分だけではなく、int型の変数の宣言部分もautoで置き換えているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、autoは型を指定する部分で、かなり便利に使うことができるのですわ

インスタンスの宣言にauto

\footnotesize \textcolor{pink}{四国めたん:} 特にautoは、クラスのインスタンスをnewを使って生成する場合に重宝しますわ

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} 今までは、クラスのインスタンスをnewを使って生成する場合はこのようにしていましたわ

クラス名* インスタンス名 = new クラス名(実際の引数...);

\footnotesize \textcolor{pink}{四国めたん:} 一目瞭然ですが、クラス名を2度、書く必要がありましたわ

\footnotesize \textcolor{lime}{ずんだもん:} ネームスペースを含めたクラス名などは、かなりの長さになるので、結構、煩わしいと思っていたのだ

\footnotesize \textcolor{pink}{四国めたん:} autoを使うことができれば、クラス名を1度のみ書けばいいので、かなり楽になりますわ

auto インスタンス名 = new クラス名(実際の引数...);

\footnotesize \textcolor{pink}{四国めたん:} なお、auto*とすることで、コンパイラにポインタであることを示唆することも可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} ふむ

\footnotesize \textcolor{pink}{四国めたん:} また、参照の場合にはauto&とすることも可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} とはいえ、コンパイラの型の推論も完ぺきではないので、時々は間違えますわ

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、ですので、あまりautoに頼るのも危険ですわね

\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、Visual Studioであれば、変数の上にカーソルを置くと変数の型が表示されるので、確認することをお勧めしますわ

\footnotesize \textcolor{lime}{ずんだもん:} お~、便利なのだ

\footnotesize \textcolor{pink}{四国めたん:} まぁ、 ラムダ式 も含めて型を正確に表示してくれない場合もあるので、あまり頼りになりませんが...

autoの歴史

autoは、C言語でもC++言語でも、もともと 自動変数 を表すキーワードでした

自動変数 とは、関数内で宣言される変数のことです

例えば以下のようなプログラムを考えます

#include <iostream>

int main(int argc, char* argv[]) {
  int a = 1;
  std::cout << a << std::endl;
  return 0;
}

ここで、変数"a"が 自動変数 です

関数内で必要になった時に 自動的 にメモリに配置され、関数を抜けて不必要になると 自動的 にメモリから破棄されるので、この名前になったようです

実は、C言語やC++言語が最初に作られた時の 自動変数 の宣言は、正式には以下のような形式でした

#include <iostream>

int main(int argc, char* argv[]) {
  auto int a = 1;
  std::cout << a << std::endl;
  return 0;
}

はい、型の前にautoを付加しています

C++11以前のコンパイラでは、正常に動作するはずです

ただ、autoは省略できるため、ほとんど全てのプログラマーは面倒なのでautoを記述しません

そこでC++11以降では、autoの意味が変更されて 型の推論 のためのキーワードとしました

当然、 自動変数 の意味はなくなりました

もし、C++11以降で 自動変数 の意味でautoを使うとエラーとなります

ちなみにC言語でもC23からauto型の推論 のためのキーワードになりました

まとめ

\footnotesize \textcolor{pink}{四国めたん:} お疲れ様ですわ

\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ

\footnotesize \textcolor{pink}{四国めたん:} 以上で ラムダ式の代入とauto について終了しますわ

\footnotesize \textcolor{pink}{四国めたん:} 次回は関数の引数に ラムダ式 を割り当てる方法についてお話ししますわ

\footnotesize \textcolor{lime}{ずんだもん:} 待っているのだ

\footnotesize \textcolor{pink}{四国めたん:} また、クラスのメソッド内の ラムダ式 からメンバーにアクセスする方法についてもお話ししますわ

\footnotesize \textcolor{lime}{ずんだもん:} 楽しみなのだ

Discussion