C++ 基礎からマスターまで — 第13回
テンプレートでタイプが異なる重複コードを減らす
本ブログ作成文は、講義を目指して製作されました。 また、作成された文章の情報も間違っている可能性がありますので、ご注意ください。
アイテムIDを探す関数とモンスターIDを探す関数がほぼ同じように見える場合がある。 データ型だけが異なり、動作が同じであれば、コードをコピーするよりもテンプレートで共通のルールを作成できる。
関数テンプレート
2つの値のうち大きい方を返す関数から作成しましょう。
template <typename T>
T Max(const T& a, const T& b)
{
return a < b ? b : a;
}
int highLevel = Max(10, 20);
float highDamage = Max(12.5f, 8.0f);
Tは、関数を呼び出すときに実際の型に変わる場所です。 コンパイラはMax<int>とMax<float>同様に必要な関数をそれぞれ作り出す。
ただMax(10, 3.5f)同様に異なるタイプを渡すと、1つのTを決定できず、コンパイルに失敗します。 明示的にタイプを合わせるか、2つのタイプを受け取るテンプレートを別々に設計する必要があります。
クラステンプレート
同じサイズのスロットを複数のタイプに使用したい場合は、クラスもテンプレートにすることができます。
template <typename T, std::size_t Size>
class Inventory
{
public:
T& At(std::size_t index)
{
return slots.at(index);
}
const T& At(std::size_t index) const
{
return slots.at(index);
}
private:
std::array<T, Size> slots{};
};
Inventory<Item, 20> itemInventory;
Inventory<Skill, 8> skillInventory;
Tは型で、Sizeはコンパイル時に決定される値です。 std::arrayのサイズはランタイムに変わらないので、固定スロットを表現するのが良い。
テンプレートとヘッダーファイル
一般関数は宣言をヘッダーに入れ、定義をcppに入れることができます。 しかし、テンプレートは、コンパイラが実際の型に合ったコードを作成する時点で定義を見ることができるはずです。
// Inventory.h
template <typename T>
class Inventory
{
public:
void Add(const T& value)
{
values.push_back(value);
}
private:
std::vector<T> values;
};
したがって、テンプレート定義は通常ヘッダーに作成されます。 宣言だけをヘッダに入れて定義をcppで隠すと、特定のタイプのコードが見つからず、リンクエラーが発生する可能性があります。
テンプレートを使用する基準
タイプだけが違って意味が同じコードならテンプレートが重複を減らす。 逆に動作と責任まで他のコードを無理やりひとつのテンプレートに入れると条件文と特殊処理が増える。
テンプレートはすべてのコードを一般化する機能ではありません。 同じルールを複数のタイプに安全に適用し、間違った組み合わせはコンパイル段階で防ぐツールだと思うほうがいい。