久しぶりにテンプレート関数を使おうとしたときのいくつかの間違いを覚書として書いておきます。
Utilityクラスなるものを用意し、次のような静的メソッドを宣言、定義しました。
Utility.h
template<typename T> static void Utility::releaser(std::vector<T*>& v);
Utility.cpp
template<typename T> void Utility::releaser(std::vector<T*>& v) { std::vector<T*>::iterator it; it = v.begin(); while(it != v.end()){ delete (*it); it = v.erase(it); } }
これをコンパイルすると、
.cpp: 静的メンバ関数 ‘static void Utility::releaser(std::vector<T*>&)’ 内: .cpp:???:???: エラー: need ‘typename’ before ‘std::vector<T*>::iterator’ because ‘std::vector<T*>’ is a dependent scope .cpp:???:???: エラー: expected ‘;’ before ‘it’ .cpp:???:???: エラー: ‘it’ was not declared in this scope
といったエラーがでます。
because ‘std::vector
はstd::vector
(VC++の場合は、問題なくコンパイルが通るらしいです。)
need ‘typename’ before ‘std::vector
型だと分かるように「typename」をつけてください、と言っています。
この時点でこのコンパイラ自身は型だとわかっているようですが、どんなコンパイラでも通るように厳しく戒めてくれている、という理解です。
Utility.cpp
template<typename T> void Utility::releaser(std::vector<T*>& v) { typename std::vector<T*>::iterator it; it = v.begin(); while(it != v.end()){ delete (*it); it = v.erase(it); } }
言われた通りにつけてみましたが、今度は、
Hoge.o: In function `hogehoge(void)': Hoge.cpp:???: undefined reference to `void Utility::releaser<Hoge>(std::vector<Hoge*, std::allocator<Hoge*> >&)'
と怒られてしまいました。
これは実際にHoge.cppでUtility::releaserを使用している個所でその実体が何か分からない、ということを言っています。
Utility.cppにおいて、クラスHogeを考慮したテンプレート関数の実体化を行っていない、かつ、Hoge.cppでも実体化を行っていないためです。
そこで、Hoge.cppで実体化が行われるように、Utility.h、つまりヘッダにテンプレートの実装を書きます。
Utility.cppの実装は消去し、
Utility.h
template<typename T> static void Utility::releaser(std::vector<T*>& v) { typename std::vector<T*>::iterator it; it = v.begin(); while(it != v.end()){ delete (*it); it = v.erase(it); } };
これで、テンプレート関数が使えます。
どうしても定義と実装を分けたい場合は、
明示的実装というものがあり、分けることもできるそうです。
予めテンプレートの使用パターンを宣言しておくというやり方です。
また、exportという記述子があり、定義と実装を分け、予めのパターン列挙も必要ないそうです。
ただし、こちらは対応コンパイラが少ないそうです。
このあたりのお話は、こちらが参考になりました。
http://d.hatena.ne.jp/pknight/20090826/1251303641
http://d.hatena.ne.jp/pknight/20090826/1251307608