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

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

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

/// myTemplate.hpp
#ifndef __MYTEMPLATE_HPP__
#define __MYTEMPLATE_HPP__

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

#endif // end of __MYTEMPLATE_HPP__
/// myTemplate.cpp
#include "myTemplate.hpp"

template <typename T>
T square(const T x)
{
return x * x;
}
/// 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;
}

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

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

/// myTemplate.hpp
#ifndef __MYTEMPLATE_HPP__
#define __MYTEMPLATE_HPP__

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

#endif // end of __MYTEMPLATE_HPP__

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

// 関数テンプレートの宣言後に明示的インスタンス化
/// 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);
// 別のファイルで明示的インスタンス化
/// main.cpp
#include "myTemplate.cpp"

template int square(const int x);
template float square(const float x);
template double square(const double x);

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

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s