🙆

[C++]テンプレートの特殊化を学ぶ

2023/01/29に公開

本日はテンプレートの特殊化をアウトプットテーマとしてやってみる。
特殊化について学ぶ前にまずはテンプレート関数を復習する。

テンプレート関数

テンプレート関数というのは言わば、1つの関数定義で様々な型の引数に対応することができる便利な関数だ。

例えば、以下のように引数を1つ取り、2倍にして返すテンプレート関数Functionを作ったとしよう。

template <typename T>
T Function(T param)
{
    return param * 2;
}

この関数を1つ作るだけで、以下のように色々な型を引数に渡して使うことができる。
わざわざint型の関数、short型の関数を定義する必要なんてないので冗長なコードを書かなくて済む。

int a = 1;
int ret1;
ret1 = Function<int>(a); // int型で渡す

short b = 1;
short ret2;
ret2 = Function<short>(b); // short型で渡す

特殊化

テンプレートを使用していると、ある型の引数を受け取った場合だけふるまいを変えたいということがたまにある。

例えば引数で受け取った値の2倍を返すような関数をテンプレートで考えてみよう。
intやlong等の数値型が引数で指定された場合はそのまま2倍すればよいが、仮に文字列が引数で指定された場合はそのままだと実行時にエラーになってしまう。

// 特殊化では無いやり方
template <typename T>
T Function(T param)
{
return param * 2;
}

// OK (ret1:2)
int ret1 = Function<int>(1);

// NG:これが書かれていると実行時にエラーになる
std::string ret2 = Function<std::string>("hoge");

このような場合テンプレートの特殊化を使うことでstring型の時だけふるまいを変えるということができ、エラーを回避できるようになる。
以下はstring型だけ特殊化した例。

template <typename T>
T Function(T param)
{
    return param * 2;
}

// string型だけ特殊化
template <>
string Function<string>(string param)
{
    return param;
}

string型が指定された場合は特殊化された方の関数が呼ばれるので、2倍する処理が呼ばれず実行時にエラーにもならない。

int ret1   = Function<int>(1);   // ret1:2
short ret2 = Function<short>(1); // ret2:2
std::string ret3 = Function<std::string>("hoge"); // ret3:"hoge"

特殊化も完全特殊化と部分特殊化の2種類がある模様。
今回紹介したのは完全特殊化と呼ばれるやり方の方なので、部分特殊化については勉強次第またアウトプットしていく予定。

Discussion