I am Charmie

メモとログ

templateの明示的インスタンス化 (explicit instantiation)

templateの明示的インスタンス化とは,宣言と定義が別ファイルに記述してある関数テンプレート・クラステンプレートを呼ぶ時に必要. コンパイラが関数テンプレートやクラステンプレートをインスタンス化する時,どのようなテンプレート引数でインスタンス化されるのかコンパイラが知っておく必要がある.関数・クラステンプレートの宣言・定義を別ファイルに分けた場合,関数・クラスの定義とその関数・クラスを使用する,すなわち使用されるテンプレート引数を知っているファイルが別々に別れていることになる.そのため,リンカエラーが起きる.

例として,入力引数の二乗を計算するsquare関数を関数テンプレートとして,以下のように別のファイルに宣言・定義してみる.このコードをコンパイルすると,main.cppの関数テンプレートを呼ぶ行でリンカエラー「LNK2019: unresolved external symbol」が発生する.

[code lang="cpp"] /// myTemplate.hpp

ifndef MYTEMPLATE_HPP

define MYTEMPLATE_HPP

template <typename T> T square(const T x);

endif // end of MYTEMPLATE_HPP

[/code]

[code lang="cpp"] /// myTemplate.cpp

include "myTemplate.hpp"

template <typename T> T square(const T x) { return x * x; } [/code]

[code lang="cpp"] /// main.cpp

include <iostream>

include "myTemplate.hpp"

int main() { int x_i = 3; float x_f = 3.5; double x_d = 4.1;

std::cout << x_i << "^2 = " << square(x_i) << std::endl; std::cout << x_f << "^2 = " << square(x_f) << std::endl; std::cout << x_d << "^2 = " << square(x_d) << std::endl;

return 0; } [/code]

解決策は幾つかあるが,どれも一長一短.

  1. 包含モデル (inclusion model) テンプレートを宣言するヘッダ内に定義を記述することで,テンプレートが全てインスタンス化されることを保証する.非常に単純な解決策. 問題点はヘッダファイルをインクルードするコストが増える,つまりコンパイルにかかる時間が長くなる点.

[code lang="cpp"] /// myTemplate.hpp

ifndef MYTEMPLATE_HPP

define MYTEMPLATE_HPP

template <typename T> T square(const T x) { return x * x; }

endif // end of MYTEMPLATE_HPP

[/code]

  1. 明示的インスタンス化 (explicit instantiation) 手動でテンプレートをインスタンス化する.明示的なインスタンス化を行うには,templateキーワードの後にインスタンス化したいテンプレート引数を全て記述する宣言をする.(1)関数テンプレートの宣言後に明示的インスタンス化を行うか,(2)別のファイルで明示的インスタンス化を行うか,どちらでも良い. 欠点は,関数・クラステンプレートがどの型でインスタンス化されたか常に意識しなければいけない点,テンプレート化したのに明示的にインスタンス化しなければいけない,つまり型を意識しなければいけないのは矛盾しているように思える点.

[code lang="cpp"] // 関数テンプレートの宣言後に明示的インスタンス化 /// myTemplate.cpp

include "myTemplate.hpp"

template <typename T> T square(const T x) { return x * x; }

template int square(const int x); template float square(const float x); template double square(const double x); [/code]

[code lang="cpp"] // 別のファイルで明示的インスタンス化 /// main.cpp

include "myTemplate.cpp"

template int square(const int x); template float square(const float x); template double square(const double x); [/code]

関数テンプレートに限らず,クラステンプレートも明示的インスタンス化ができる. クラステンプレートをインスタンス化すると,自動的に全てのメンバ関数インスタンス化される. クラステンプレートの一部のメンバ関数のみをインスタンス化することも可能.