分割コンパイルでtemplateの部分特殊化の扱い

昨日、分割コンパイルして制作しているコードのビルドをした時にVS2010(VC++2010)においてtemplateの部分特殊化を使ったコードの再定義エラー及び外部参照エラーが起きたのでその解決方法について記述しておく。

以下のファイルにおいて外部参照エラーと再定義エラーが起こる。原因であるところにコメントを付けておいた。

class.h

#ifndef __CLASS_H
#define __CLASS_H

struct Hoge//※
{
	template<class T>
	void foo(const T &t){}
	
	template<>
	void foo(const int& integer){}
};

template<class T>
void foo(const T &t)
{
}

template<>
void foo(const int &integer) //多重定義エラー:使用するファイル各々のobjファイルでfoo<int>の部分特殊化がされた実体が作られる
{
}

template<class T>
void prot();   //外部参照エラー:宣言だけだと実体を作れない
#endif

class.cppにもちろんprotの定義はある

#include"class.h"

template<class T>
void prot()
{
}

呼び出す側の記述。sub.cppとmain.cpp

#include"class.h"

void fuga()
{
	Hoge h;

	h.foo<int>(1);
	h.foo<double>(3.25);

	foo<double>(4.52);
	foo<int>(0);

	prot<int>();

}
#include"class.h"

int main()
{
	Hoge h;

	h.foo<int>(1);
	h.foo<double>(3.25);

	foo<double>(4.52);
	foo<int>(0);

	prot<int>();

	return 0;
}

のようになっている。

これを解決するには、多重定義になってしまう特殊化の記述はcppファイルに定義を書き、ヘッダファイルには宣言だけにしておくこと(template<>void foo(const int&);)。それ以外は外部参照エラーになるのでヘッダに定義を全て記述する必要がある。
また、クラス内のtemplate関数については、部分特殊化に問わず全てヘッダに記述しなければ外部参照エラーとなる。クラス自体がstaticでない限り、objファイル作成時に実体が作られることが無いからではないかと考えられる。template<...>class ....にした時の検証はまだしていない。
inline関数の場合は調べたところ、部分特殊化についてもこの限りでは無いらしい(ヘッダに書いても良いみたい(書かないといけない?))。

今日はこの辺で。また似たようなことに引っかかったらここで確認して新しいことがわかったらまた追記/記事を書きたいかな。