生きる

助けて

関数テンプレートの明示的実体化と実体の宣言

明示的実体化を説明する前に実体化の意味をはっきりさせておく必要がある。似た言葉に特殊化があるが、特殊化と実体化は異なる。まずはその違いを説明する。

以下を例に考える。

template<typename T>
T calc(T a, T b) { return a + b; }

実体化の方がわかりやすい。実体化は、calc<int>(1, 2)のような呼び出しがあるソースコードコンパイルしたときに、

int calc(int a, int b) { return a + b; }

に相当する関数定義を生成することである。このとき生成される関数を実体と呼ぶ。

これに対し特殊化は、

// (a)
template<typename T>
T calc(T a, T b) { return a + b; }

// (b)
template<>
double calc<double>(double a, double b) { return a - b; }

のように、ある関数テンプレート(ここではcalc)に対して、「基本は(a)の通りに実体化してほしいけれど、もし特定の型(ここではdouble)が与えられた場合は(b)のように実体化してね」とソースコード中で指示することをいう。

これを踏まえた上で、明示的実体化とは、calc<int>(1, 2)のような呼び出しの有無に関係なく、ユーザーの指示によってT=intの実体を生成することをいう。明示的実体化を行うには、ソースコード中に次のように記述する。

template<typename T>
T calc(T a, T b) { return a + b; }

template int calc<int>(int a, int b); // T=intの実体が作られる

明示的実体化と対比して、calc<int>(1, 2)のような呼び出しによって起こる実体化を暗黙的実体化という。

明示的実体化は、実体の宣言と組み合わせることで効果的に使用できる。実体の宣言を行うには以下のように記述する。

template<typename T>
T calc(T a, T b) { return a + b; }

extern template int calc<int>(int a, int b); // 宣言

calc<int>(1, 2); // このファイルのコンパイルでは実体化されない

宣言された実体は、たとえ元になる関数テンプレートの定義が見えていたとしても、暗黙的には実体化されない。なぜなら、実体を宣言することは、その実体がどこかで明示的に実体化されることをプログラマが保証することだからである。

よく使うことがわかっている実体を宣言しておけば、その実体をたくさんのファイルから呼び出していても、実体化はあるファイルで1度明示的に行うだけで済む。これによりコンパイル時間を削減できる。