C++テンプレートを使いこなす! 第一部 (2)
(2) テンプレートはヘッダーに書こう
通常の関数の場合,プロトタイプ宣言と定義を二つのファイルに分けて書くのが普通です.つまり,
min.h
001 int min(int lhs, int rhs);
min.cpp
001 #include "min.h"
002 int min(int lhs, int rhs)
003 return (lhs<rhs?lhs:rhs);
004 }
main.cpp
001 #include <cstdio>
002 #include "min.h"
003 int main(int argc, char* argv[])
004 {
005 printf("%d\n", min(10,20));
006 return 0;
007 }
上記のようなファイルを用意して,
> g++ main.cpp min.cpp
と,コンパイラに関数定義を書いた実装ファイル(*.cpp)をすべて渡してコンパイルします.ヘッダーファイル(*.h)は実装ファイルの中で#includeしていますから,プリプロセッサが#includeの部分に単純に挿入してくれますので,コンパイラに与える必要はありませんし,与えてはいけません.
このように,通常の関数の宣言と定義を別のファイルにして,宣言のみが書かれたヘッダーファイルを実装ファイルにおいて#includeすることで,ソースファイルの依存関係を小さく保つことができます.もし,min.cppの中身が何らかの理由で書き換えが必要になっても,プロトタイプ宣言のmin.hの部分まで変更を受けない限り,変更の余波がmain.cppに達することはありません.なぜなら,ソースファイル間の依存関係が
main.cpp -> min.h
min.cpp -> min.h
のようになっているからです.main.cppがmin.cppのことを知らなくても,min.hに書かれた情報さえあればコンパイルできるのです.このことをうまく利用すれば,C++で作った大きなソフトウェアのコンパイル時間を大幅に短縮することができます.
なお,わざと宣言と定義を分けずに,宣言と同時に定義もしてしまうこともあります(inline関数).それはそれで利点があるのですがここでは説明を省きます.
さて,それでは,関数テンプレートの場合でも宣言と定義を分けて書いてみましょう.
min_t.h
001 template<typename T> T min(T lhs, T rhs);
min_t.cpp
001 #include "min_t.h"
002 template<typename T> T min(T lhs, T rhs)
003 return (lhs<rhs?lhs:rhs);
004 }
main_t.cpp
001 #include <cstdio>
002 #include "min_t.h"
003 int main(int argc, char* argv[])
004 {
005 printf("%d\n", min(10,20));
006 return 0;
007 }
通常の関数でのやり方に習って書くと,こんな感じでしょうか.ところが,これはコンパイルすることができません.なぜなら,関数テンプレートmin<>()の利用者側であるmain_t.cppは,min_t.hに書かれた情報だけではコンパイルできないからです.関数テンプレートをコンパイルするためには,その定義も必要なのです.
従って,正しくはこのように書くことになります.
min_t2.hpp
001 template<typename T> T min(T lhs, T rhs)
002 return (lhs<rhs?lhs:rhs);
003 };
main_t2.cpp
001 #include <cstdio>
002 #include "min_t2.hpp"
003 int main(int argc, char* argv[])
004 {
005 printf("%d\n", min(10,20));
006 return 0;
007 }
拡張子hppは,ヘッダーと実装を両方含んでいるんだよ,と人間にわかりやすくするためであり,それ以上の意味はありません.言語仕様としては何でもよいです.
ただし,言語仕様上はヘッダーではなく実装の方に書く方法もあるようです(exportテンプレート).しかし,対応しているコンパイラがほとんどなく,現実的にはヘッダーに書くことになりますので,ここでは説明を省くことにします.
では,今回のまとめです.
1. テンプレートはヘッダーに書こう.
-
-
- -
-