🖥️

【C++言語入門】 第21回 関数テンプレート

に公開

https://youtu.be/BVBM6CMcaY8

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

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

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

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

\footnotesize \textcolor{pink}{四国めたん:} 今回は テンプレート についてお話ししますわ

\footnotesize \textcolor{lime}{ずんだもん:} テンプレート

\footnotesize \textcolor{pink}{四国めたん:} はい、 テンプレート はC++言語における基本的な機能の一つですわ

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

\footnotesize \textcolor{pink}{四国めたん:} 実際、C++言語で一般的に添付される標準ライブラリである標準テンプレートライブラリ(STL)では、名前の通り テンプレート を最大限に使っていますわ

\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}{四国めたん:} 以前にint型とfloat型に対して オーバーロード を使用してadd関数を定義していましたわ

\footnotesize \textcolor{lime}{ずんだもん:} 覚えているのだ

float add(float a, float b) {
  float c = a + b;
  return c;
}

int add(int a, int b) {
  int c = a + b;
  return c;
}

\footnotesize \textcolor{pink}{四国めたん:} この関数を見る限り、使っている型がfloatintかの違いはあるものの、関数の定義の構造としては同じとなっていますわ

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

\footnotesize \textcolor{pink}{四国めたん:} オーバーロード を使えば、intfloat以外にも、必要な型の数だけ関数を定義することができますわ

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

\footnotesize \textcolor{pink}{四国めたん:} ただ、必要な型がdoublelong longなどと増えていくと、 オーバーロード で関数を定義していくのも面倒ですわね

\footnotesize \textcolor{lime}{ずんだもん:} 複雑な処理が必要な関数は特に面倒なのだ

\footnotesize \textcolor{pink}{四国めたん:} そこで、関数の の部分に 仮の型 を指定する方法が提供されましたわ

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

\footnotesize \textcolor{pink}{四国めたん:} はい、関数を呼び出す際に実際の を指定することで、関数の定義を1つにまとめてしまうことができるのですわ

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

\footnotesize \textcolor{pink}{四国めたん:} このような仕組みを テンプレート といいますわ

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

\footnotesize \textcolor{pink}{四国めたん:} 関数に対しての テンプレート を使った定義は以下の通りですわ

template<class テンプレート引数1, class テンプレート引数2, ...>
戻り値の型 関数名(引数...) { 処理 };

\footnotesize \textcolor{lime}{ずんだもん:} かなり複雑に見えるのだ

\footnotesize \textcolor{pink}{四国めたん:} まず、templateキーワードに続けて山括弧"<>"内に テンプレート引数 をリストアップしますわ

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

\footnotesize \textcolor{pink}{四国めたん:} その際、 テンプレート引数 の名前の前にclassキーワードを付加したものを 型引数 と言いますわ

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

\footnotesize \textcolor{pink}{四国めたん:} ちなみに、最近ではclassキーワードの代わりにtypenameキーワードを付加することも増えていますわ

\footnotesize \textcolor{lime}{ずんだもん:} 違いは何なのだ?

\footnotesize \textcolor{pink}{四国めたん:} ほとんど違いはありませんので、どちらを使ってもOKですわ

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

\footnotesize \textcolor{pink}{四国めたん:} そして 型引数戻り値の型引数の型 、処理中の型指定などに使うことができますわ

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

\footnotesize \textcolor{pink}{四国めたん:} なお 型引数 は複数指定することが可能ですわ

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

\footnotesize \textcolor{pink}{四国めたん:} そして関数を呼び出す際に 実際の型 を指定しますわ

関数名<実際の型1, 実際の型2, ...>(実際の引数...);

\footnotesize \textcolor{lime}{ずんだもん:} つまり テンプレート で定義された関数を呼び出す場合には、関数名に続けて山括弧"<>"内に 実際の型 をリストアップすると云うことか

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

\footnotesize \textcolor{pink}{四国めたん:} 実際に オーバーロード で使用したプログラム例を テンプレート を使って書き換えてみましょう

#include <iostream>

template <class T>
T Add(T a, T b) {
  T c = a + b;
  return c;
}

int main(int argc, char* argv[]) {
  int x = 1;
  int y = 2;
  int z = Add<int>(x, y);
  float d = 3.0;
  float e = 4.0;
  float f = Add<float>(d, e);
  std::cout << "xとyの和は" << z << "です" << std::endl;
  std::cout << "dとeの和は" << f << "です" << std::endl;
  return 0;
}

テンプレート

\footnotesize \textcolor{lime}{ずんだもん:} 問題なく動作しているのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに 型引数 は、一般的にはパスカルケースを使いますわ

\footnotesize \textcolor{lime}{ずんだもん:} クラス名の場合といっしょなのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、特に大文字の一文字、例えば"T"や"U", "V"...などが好まれるようですわ

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

\footnotesize \textcolor{lime}{ずんだもん:} ところでプログラムについてもう少し詳しく教えて欲しいのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

\footnotesize \textcolor{pink}{四国めたん:} まず、Addテンプレート の定義については、template <class T>として テンプレート型引数 を指定した後、Add関数の型を 型引数 で置き換えていますわ

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

\footnotesize \textcolor{pink}{四国めたん:} そしてAdd関数の呼び出し側では、Addの後に山括弧"<>"を使ってintfloatの型の指定をおこなっていますわ

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

型引数のデフォルトの型

\footnotesize \textcolor{pink}{四国めたん:} 関数の引数はデフォルトの値を持つことができるのを覚えていますか?

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} テンプレート型引数 も同様に、デフォルトの型を持つことができますわ

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

\footnotesize \textcolor{pink}{四国めたん:} 形式は、関数の引数のデフォルト値の指定と同様に、 型引数 の後に= デフォルトの型として指定を行いますわ

template<class 型引数1, class 型引数2 = デフォルトの型2, ...>
戻り値の型 関数名(引数...) { 処理 };

\footnotesize \textcolor{lime}{ずんだもん:} お~、たしかに関数の引数のデフォルト値の指定と似ているのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、デフォルトの型を指定した後の 型引数 は、全てデフォルトの型を持つ必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} その辺りも関数の引数と同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} プログラム例を書き換えてみましょう。

#include <iostream>

template <class T = int>
T Add(T a, T b) {
  T c = a + b;
  return c;
}

int main(int argc, char* argv[]) {
  int x = 1;
  int y = 2;
  int z = Add<>(x, y);
  float d = 3.0;
  float e = 4.0;
  float f = Add<float>(d, e);
  std::cout << "xとyの和は" << z << "です" << std::endl;
  std::cout << "dとeの和は" << f << "です" << std::endl;
  return 0;
}

デフォルト

\footnotesize \textcolor{lime}{ずんだもん:} 詳しい説明をお願いするのだ

\footnotesize \textcolor{pink}{四国めたん:} まず、template <class T = int>でデフォルトの型をintに指定していますわ

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

\footnotesize \textcolor{pink}{四国めたん:} 呼び出し側ではint z = Add<>(x, y);と山括弧"<>"内のint型の指定を省略できていますわ

\footnotesize \textcolor{lime}{ずんだもん:} 山括弧"<>"は省略できないのだな

\footnotesize \textcolor{pink}{四国めたん:} そうでもありませんわよ

型指定の省略

\footnotesize \textcolor{pink}{四国めたん:} それでは テンプレート を使った関数に対して、従来の関数の呼び出しをおこなってみましょう

#include <iostream>

template <class T>
T Add(T a, T b) {
  T c = a + b;
  return c;
}

int main(int argc, char* argv[]) {
  int x = 1;
  int y = 2;
  int z = Add(x, y);
  float d = 3.0;
  float e = 4.0;
  float f = Add(d, e);
  std::cout << "xとyの和は" << z << "です" << std::endl;
  std::cout << "dとeの和は" << f << "です" << std::endl;
  return 0;
}

推測

\footnotesize \textcolor{pink}{四国めたん:} とりあえず 型引数 のデフォルトの型は指定しないようにしますわ

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

\footnotesize \textcolor{pink}{四国めたん:} そしてAddの呼び出し側では山括弧"<>"を削除して実際の型の指定をおこなっていませんわ

\footnotesize \textcolor{lime}{ずんだもん:} でも、プログラムはエラーも出さずに正常に動作しているのだ

\footnotesize \textcolor{pink}{四国めたん:} 実は、引数に 型引数 を使用している場合、関数の呼び出し側の引数から推測可能な場合には、山括弧"<>"を使って型の指定を行わなくとも良いことになっていますわ

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

\footnotesize \textcolor{pink}{四国めたん:} 最近のコンパイラはすごく進化していますわね

\footnotesize \textcolor{lime}{ずんだもん:} たしかにすごいのだ

非型引数を用いた関数テンプレート

\footnotesize \textcolor{pink}{四国めたん:} テンプレート を定義する際に山括弧"<>"内でリストアップされる引数は、総称して テンプレート引数 と呼ばれますわ

\footnotesize \textcolor{lime}{ずんだもん:} 型引数テンプレート引数 の一種ということか

\footnotesize \textcolor{pink}{四国めたん:} では 型引数 ではない テンプレート引数 、いわゆる 非型引数 はどのように指定するのでしょうか?

template<型 テンプレート引数1, 型 テンプレート引数2, ...>
戻り値の型 関数名(引数...) { 処理 };

\footnotesize \textcolor{lime}{ずんだもん:} これも結構、複雑な感じがするのだ

\footnotesize \textcolor{pink}{四国めたん:} templateキーワードに続く山括弧"<>"内にclasstypenameの代わりに、例えばintなどの型を指定し、続けて テンプレート引数 の名前を指定しますわ

\footnotesize \textcolor{lime}{ずんだもん:} なんとなく通常の関数の引数の指定と同じ感じがするのだ

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

\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}{四国めたん:} なお、関数の呼び出しは、このようにしますわ

関数名<実際の値1, 実際の値2, ...>(実際の引数...);

\footnotesize \textcolor{pink}{四国めたん:} 実際にプログラム例を見てみましょう

#include <iostream>

template <typename T, int multi>
T MultiAdd(T a, T b) {
  T c = (a + b) * multi;
  return c;
}

int main(int argc, char* argv[]) {
  int x = 1;
  int y = 2;
  int z = MultiAdd<int, 10>(x, y);
  float d = 3.0;
  float e = 4.0;
  float f = MultiAdd<float, 10>(d, e);
  std::cout << "xとyの和は" << z << "です" << std::endl;
  std::cout << "dとeの和は" << f << "です" << std::endl;
  return 0;
}

テンプレート引数

\footnotesize \textcolor{pink}{四国めたん:} 関数テンプレート としてMultiAddを定義していますわ

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

\footnotesize \textcolor{pink}{四国めたん:} Add関数との違いは、 型引数 "T"以外に 非型引数 "multi"を指定していますわ

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

\footnotesize \textcolor{pink}{四国めたん:} Addでは関数の引数"a"と"b"の和を返していましたが、MultiAddでは更に"multi"をかけてから返していますわ

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

\footnotesize \textcolor{pink}{四国めたん:} 呼び出し側ではint z = MultiAdd<int, 10>(x, y);のように、 型引数 "T"の指定とともに テンプレート引数 "multi"に10を指定していますわ

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

\footnotesize \textcolor{pink}{四国めたん:} なお、これだけであれば テンプレート は関数の引数とほとんど違いはありませんわね

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

\footnotesize \textcolor{pink}{四国めたん:} この形式の テンプレート引数 は、次回に説明する クラステンプレート で威力を発揮しますわ

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

まとめ

\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした

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

\footnotesize \textcolor{pink}{四国めたん:} 以上で 関数テンプレート のお話を終わりますわ

Discussion