テンプレート入門


概要

C++のテンプレートとはクラスのメンバ変数や関数の引数や戻り値を
定義する段階であいまいな状態で定義できる機能です。
プログラムではメンバや関数のアルゴリズムが同じでもデータ型のみを変えたいという
状況が多々あるので、そういった際にこのテンプレート機能を使用することで
汎用的なクラスや関数を作成することができます。

// 書式例
template <typename パラメータ名>
パラメータ名 関数名(引数)
{
}

// 具体例
template <typename T>
T AddSum(T x, T y)
{
	return (x + y);
}

void main(void)
{
	printf("int AddSum(10, 10) => %d\n", AddSum(10, 10));
	printf("float AddSum(20.0f, 20.0f) => %f\n", AddSum(20.0f, 20.0f));
}

実行結果:
	int AddSum(10, 10) => 20
	float AddSum(20.0f, 20.0f) => 40.0


具体例でAddSumの戻り値、引数にテンプレートパラメータ「T」を使用しています。
このAddSumは関数定義時点では戻り値、引数のデータ型はあいまいな状態です。
AddSumのデータ型はmain関数のAddSum関数呼び出しの部分で決まります。
コンパイラが関数呼び出しの部分を読み取り、設定されているデータ型で
関数を実行するように対応してくれます。
テンプレートを使用すると関数のオーバーロードを作成しなくとも
同様の効果を生み出す処理を作成することができます。

テンプレートクラス

テンプレートクラストとはクラスのメンバに対してテンプレートを使用したクラスです。

// 宣言書式例:
template <class T>
class クラス名
{
	T メンバ変数;

	T 関数名(引数)
	{
	}
};

// 宣言具体例:
template <class T>
class Position
{
public:
	T GetPosX()
	{
		return m_PosX;
	}

	T GetPosY()
	{
		return m_PosY;
	}

	void SetPosX(T posx)
	{
		m_PosX = posx;
	}

	void SetPosY(T posy)
	{
		m_PosY = posy;
	}

private:
	T m_PosX;
	T m_PosY;
};

宣言はclass宣言のすぐ前でテンプレート関数と同じように、
template <パラメータ名>の指定を行います。
そして、宣言されたテンプレートのパラメータを使用したいメンバの型で使用します。
次は宣言したテンプレートクラスを変数として使用する際の書式です。

// 変数宣言の書式:
クラス名<データ型> 変数名;

// 変数宣言の具体例
// メンバをint型として扱う
Position<int> i_pos;

// メンバをfloat型として扱う
Position<float> f_pos;

変数として使用する場合は、変数宣言時に型の指定を行います。
宣言するクラス名と変数名の間で、テンプレートのデータ型を指定します。
テンプレートを使用したクラスは汎用クラスと呼ばれています。

typenameとclassの違い

今回の説明で、関数は「typename」で、クラスでは「class」を使用していますが
どちらを使用しても効果が変わるということはありません。
ですが、「テンプレートで使用する予定の型がintやfloatなどの場合はtypename、
クラスを使用する場合はclassとする」などのルールを設けることで、
コードの可読性や保守性が上がる可能性があります。

パラメータの数

テンプレートで設定できるパラメータは1つだけではなく複数設定することが可能です。

template<typename T, typename U>
class Test
{
public:
private:
	T m_Value01;
	U m_Value02;
};

void main(void)
{
	Test<int, float> test01;
	Test<short, char> test02;
}

パラメータの名前

パラメータの名前は変数名のように自由に設定できます。
ほとんどの参考書などではTが使用されていますが、
これはTemplateのTから来ているといわれています。
また、パラメータが複数の場合はTの次はUを使用していますが、
これはアルファベットのTの次がUだからです。
多重forの場合i、j、kと同じように深く考えずに
こういうものだと考えるといいと思います。

template<typename DATA>
class Position
{
public:
private:
	DATA m_PosX;
	DATA m_PosY;
};

void main(void)
{
	Position<int> pos1;
	Position<float> pos2;
}

上のコードのようにT以外の名前を付けてもエラーにはなりません。

テンプレートクラスの関数定義(クラス外)

テンプレートクラスの関数定義をクラスの外で行う場合、
通常の「クラス名::関数名」で定義することはできません。
定義にはテンプレートクラスであることを明示する必要があります。

// 書式例:
template <typename パラメータ名>
戻り値の型 クラス名<パラメータ名>::関数名(引数)
{
	
}

// 具体例:
template<typename DATA>
class Position
{
public:
	Position();

private:
	DATA m_PosX;
	DATA m_PosY;
};

template <typename DATA>
Position<DATA>::Position()
{
	printf("Positionのコンストラクタ\n");
}