Youtube登録者5000人突破!!

【C++】オーバーロード・テンプレート 【はじめてのC++プログラミング入門講座 #9】

はじめてのC++プログラミング入門講座では、プログラミングをやったことのない人でも分かるように、C++について解説します。まずは、C++を知るところからはじめて、初心者がつまづきやすい「ポインタ」までをゴールとして進めていきます。全て無料で学べる内容となってますので、ぜひ最後までお付き合いください。

丁寧に学ぶをモットーに、ひとつずつじっくりと理解しけるように解説します。 はじめは少し退屈かもしれませんが、基礎を身に着けておくと、後が楽になります。また、プログラミング学習でよくあるのが、飛ばしすぎて途中で挫折してしまうことです。続けさえすれば力になります。じっくりゆっくり学んでいきましょう。

本講座はC++の開発環境が必要です。環境構築には第1回のVisual Studioの環境構築を参考にしてください。下記からどうぞ。

前回の第8回では、関数について解説しました。詳細は下記からどうぞ。

関数はうまく使いこなせば、一生同じプログラムを書かなくて良くなるという活気的な機能です。自分が作れるようになるのも重要ですが、他人の関数を使えるようになるだけでも十分に価値があります。

作り方は難しく感じるかも知れませんが、ぜひ構造を理解しておきましょう。

重要なポイントは下記の通りです。

  • 関数にすることで自作の機能をまとめられる
  • 関数は、引数を与えたら、処理をして、返り値を返す機能である
  • どれかの機能が抜けていても関数は作ることができる
  • 関数の形は「返り値の型 関数名(引数) { 処理 }」で書ける
  • もし返り値の型がない場合はvoidを書き、引数がない場合は何も書かなくて良い
  • プロトタイプ宣言は、関数の宣言と関数の定義を分けて書く
  • プロトタイプ宣言を使えば、メイン関数より後に自作関数の定義を書ける
  • プログラムを見やすくするために、プロトタイプ宣言を使うのが一般的である

オーバーロード・テンプレート

前回は関数について学びましたが、今回はその機能の紹介です。関数の能力はこんなものではありません。まだまだ便利になります。

関数は、引数を与えて、処理をして、返り値を返す、という機能でした。しかしここで不便なのが、引数も返り値も型を指定する必要があるということです。

int型で作った関数にdouble型の引数は渡せないし、引数の個数が変わると、別の関数を作らないといけません。この困りごとを解決するのが、今回紹介する「オーバーロード」「テンプレート」です。

オーバーロードは、関数の引数の型や数によって、別の関数を実行させられる機能です。一方で、テンプレートは、型によらず、同じ処理をする関数を作ることが出来ます。簡単に言うと、どちらも柔軟に対応できる関数ができるということです。

例えば、渡された値を全て足して返す関数を作りたいとします。そんな時、オーバーロードが便利です。オーバーロードを使えば、引数の個数が2個でも3個でも対応できる関数を作ることができるようになります。また、渡された引数がint型でもdouble型でも対応する関数も作ることが出来ます。

一方でテンプレートは、「」に特化しています。例えば、今まで使用していた演算子の + – * / は、int型もdouble型も計算できました。これは、テンプレートで、何の型が来ても計算できるようにしているからです。

テンプレートはつまづきポイントの一つです。一見すると書き方がややこしく感じるかもしれません。しかし、使いこなすと非常に便利ですので、少しずつ理解していきましょう。

オーバーロード

オーバーロードは、関数の引数の型や数によって、別の関数を実行させられる機能です。一方で、テンプレートは、型によらず、同じ処理をする関数を作ることが出来ます。簡単に言うと、どちらも柔軟に対応できる関数ができるということです。

例えば、渡された値を全て足して返す関数を作りたいとします。そんな時、オーバーロードが便利です。オーバーロードを使えば、引数の個数が2個でも3個でも対応できる関数を作ることができるようになります。また、渡された引数がint型でもdouble型でも対応する関数も作ることが出来ます。

文章だけだと理解できないと思うので、プログラムを見ながら理解していきましょう。今回扱うプログラムの全体像は下記の通りです。前回学んだ関数のプロトタイプ宣言を使用しています。

#include <iostream>
using namespace std;

int equation(int a, int b);
double equation(double a, double b);

int main()
{
	int x = 10;
	int y = 10;

	int z = equation(x, y);

	cout << z << endl;

	double xd = 1.5;
	double yd = 0.1;

	double zd = equation(xd, yd);

	cout << zd << endl;
}

int equation(int a, int b) {
	return 2 * a + b;
}
double equation(double a, double b) {
	return 2 * a + b;
}

出力結果は下記のようになります。

30
3.1

ライブラリとメイン関数

#include <iostream>
using namespace std;

・・・

int main()
{
・・・
}

・・・

今回はインクルードするライブラリは入出力ライブラリのiostreamだけです。残りのコードは全てメイン関数の中に書いていきます。

using namespace std; によって、標準ライブラリを使う際の名前空間であるstd::を省略表記できるようにしています。

オーバーロード

今回はメイン関数の中身を見る前に、自作関数の宣言と定義を見ていきましょう。

int equation(int a, int b);
double equation(double a, double b);

int main(){ ・・・ }

int equation(int a, int b) {
	return 2 * a + b;
}
double equation(double a, double b) {
	return 2 * a + b;
}

まず初めの二段で関数の宣言をしています。そしてメイン関数の下で関数の定義を行っており、どちらも全く同じ処理です。ここで重要なのが、同じ関数名であることです。

メイン関数で自作関数の呼び出しをする際に、関数名を目印として呼び出しを行っていました。もし2つ同じ関数がある場合はどのように呼び出しが変わるのでしょうか?

ここで注目してほしいのが、関数の引数の型が異なる所です。1つ目のequation関数はint型で引数を渡しているのに対して、2つ目のequation関数ではdouble型を渡しています。

このような書き方をすることで、呼び出し時に渡す変数の型に応じて、どちらの関数が実行されるかが決まるようになります。

このように、同じ関数名で型や引数が異なる関数を作ることをオーバーロードといいます。オーバーロードと使用することで、型や引数の数の変化に対して柔軟に対応できる関数を作ることが可能です。

今回は型が異なる場合を扱いますが、引数の数を変えて同じ関数を作成することも可能です。例えば、下記のように書けば、引数が2つでも3つでも総和を求められるsum関数を作成できます。

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

メイン関数(オーバーロード)

	int x = 10;
	int y = 10;

	int z = equation(x, y);

	cout << z << endl;

	double xd = 1.5;
	double yd = 0.1;

	double zd = equation(xd, yd);

	cout << zd << endl;

まず、変数xとyをint型(整数型)で宣言した場合のequation関数を実行しています。equation関数の出力は変数zとして画面出力します。

そして、浮動小数点型である変数xdとydに関しても同様の作業をしています。計算結果を画面に出力すると下記のようになります。

30
3.1

どちらも同じequation関数の呼び出しで、複数の型に対応した関数ができていることがわかります。

メイン関数から見ると、様々な型に対応できる関数として扱えるため、非常に便利です。呼び出し時に型を意識する必要がなくなり、オーバーロードは呼び出し時の負担を少なくしてくれます

関数は集団でのプログラム開発に必須です。そして、オーバーロードはそれを更に加速してくれます。普通に関数の機能だけを使う場合は、自作関数を作成するときにメイン関数からどのように呼び出すかを考えながら作る必要がありました。しかし、オーバーロードなら使いそうなパターンは型や引数の数を変更したものをまとめて作ってしまえばよいのです。

柔軟性があれば他のプロジェクトでも応用できます。例えば、合計を求める関数なんかはよく使うでしょう。多少プログラムは複雑にはなりますが、オーバーロードは非常に便利な機能であることがわかります。

テンプレート

オーバーロードは型も引数も変更できる柔軟な関数でした。しかしオーバーロードにも問題はあります。

引数の数は大体の数が決まってますし、もし引数が非常に多ければ配列を渡してしまえばよいです。一方で、どんな型でも対応できる関数を作ろうとすると果てしないです。int型、float型、double型以外にもlong int型など特殊な型は無数にあります。

どんな型でも対応できるプログラムをオーバーロードで書こうとすると、非常に長いプログラムになってしまいます。そんなときに使えるのがテンプレートです。

一方でテンプレートは、「」に特化しています。例えば、今まで使用していた演算子の + – * / は、int型もdouble型も計算できました。これは、テンプレートで、何の型が来ても計算できるようにしているからです。

テンプレートで書き直すと下記のようになります。

・・・

template <class T>
T equation(T a, T b);

int main(){ ・・・ }

template <class T>
T equation(T a, T b) {
	return 2 * a + b;
}

ライブラリとメイン関数内は全く同じ形なので、自作関数の宣言と定義だけ見ていきましょう。

関数の宣言も定義も処理の有無以外は同じ形で書かれていることがわかると思います。これはプロトタイプ宣言のやり方として解説したとおりです。

template<class T> はテンプレートの宣言と定義に必須の文です。毎回書き方は同じなので中身の理解は不要ですが、一応説明しておきましょう。

template はその名の通り、テンプレートを使用することを意味しています。三角カッコ<>内のclass Tは型を表します。classは型を意味しており、typenameと書くこともできます。どちらも型を定義する作業で、やってることは同じです。

Tは自作の型名であり、テンプレート用の型をTに与えます。int型やfloat型と同じように、テンプレートという型をTに与えています。そのため、Tは別の名前でも大丈夫です。わざわざ変える必要はないので、わかりやすいように多くの人がTを使用します。

Tが自作のテンプレート用の型デあることが分かれば、関数も理解できると思います。

T equation(T a, T b); 関数の意味としては、返り値がTの型、引数も全てTの型であることを表しています。Tはテンプレートであるため、引数の型に応じた計算を行い、引数の型に応じた結果が出力されることになります。

これで、どんな型にも対応できる関数を作成することができました。引数をint型やdouble、floatに変えて試してみてください。

おわりに

今回はオーバーロードとテンプレートについて解説しました。

どちらも柔軟な関数を作るために非常に役に立ちます。特に分業なんかしていると、テンプレートはかなり多用されます。特徴的な形なので全く知らないとパニックになりますが、知ってしまえば大したことはありません。

形を覚えて便利な関数を書けるようになりましょう。

重要な項目は下記の通りです。

  • オーバーロードは異なる型、引数の数に対応できる関数を作成できる
  • 同じ関数名を使えば、オーバーロードできる
  • 異なる型に対応できる関数を作りたいならテンプレートを使う
  • テンプレートの書き方は難しそうに見えるが、基本的にコピペでOK
  • オーバーロードとテンプレートを加えても関数呼び出しのやり方は変わらない

オーバーロードとテンプレートは知らないと他人のプログラムが読めない危険があります。「なんで二回も同じ関数を作っているんだろう?」「なんだこの文字列は?」といった疑問を持ってしまうと時間がいくらあっても作業が進みません。

一つの便利機能として理解してもらえると、他の人のプログラムを読めて、それを真似することもできます。他人のきれいなプログラムを見て吸収していきましょう。

次回は最終回で、ポインタについて解説します。ポインタはプログラミングの挫折ポイントになりがちですが、原理を理解すればそれほど難解ではありません。丁寧に理解していきましょう。

youtubeでも解説しています。こちらもどうぞ。