定期的に私を苛立たせるC++の側面は、テンプレートがヘッダーファイル(従来はインターフェイスを記述している)と実装(.cpp)ファイルの間のどこに収まるかを決定することです。多くの場合、テンプレートはヘッダーに含める必要があり、実装を公開し、以前は.cppファイルに含める必要があった余分なヘッダーをプルすることもあります。最近、この問題が再び発生しました。その簡単な例を以下に示します。
#include <iostream> // for ~Counter() and countAndPrint()
class Counter
{
unsigned int count_;
public:
Counter() : count_(0) {}
virtual ~Counter();
template<class T>
void
countAndPrint(const T&a);
};
Counter::~Counter() {
std::cout << "total count=" << count_ << "\n";
}
template<class T>
void
Counter::countAndPrint(const T&a) {
++count_;
std::cout << "counted: "<< a << "\n";
}
// Simple example class to use with Counter::countAndPrint
class IntPair {
int a_;
int b_;
public:
IntPair(int a, int b) : a_(a), b_(b) {}
friend std::ostream &
operator<<(std::ostream &o, const IntPair &ip) {
return o << "(" << ip.a_ << "," << ip.b_ << ")";
}
};
int main() {
Counter ex;
int i = 5;
ex.countAndPrint(i);
double d=3.2;
ex.countAndPrint(d);
IntPair ip(2,4);
ex.countAndPrint(ip);
}
実際のクラスを基本クラス、つまり仮想デストラクタとして使用するつもりであることに注意してください。それが重要かどうかは疑わしいですが、念のためカウンターに置いておきました。上記の結果の出力は次のとおりです。
counted: 5
counted: 3.2
counted: (2,4)
total count=3
Counter
これで、のクラス宣言はすべてヘッダーファイル(例:counter.h)に入れることができます。iostreamを必要とするdtorの実装をcounter.cppに入れることができます。countAndPrint()
しかし、iostreamも使用するメンバー関数テンプレートをどうすればよいでしょうか。コンパイルされたcounter.oの外部でインスタンス化する必要があるため、counter.cppでは使用できません。しかし、counter.hに入れるということは、counter.hを含むすべてのものにiostreamも含まれることを意味します。これは、間違っているように見えます(そして、私はこの嫌悪感を乗り越えなければならないかもしれないことを受け入れます)。テンプレートコードを別のファイル(counter.t?)に入れることもできますが、それはコードの他のユーザーにとっては少し意外なことです。Lakosは、私が望むほど実際にはこれに触れていません。C++ FAQベストプラクティスにはなりません。だから私が求めているのは:
- コードを私が提案したものに分割するための代替手段はありますか?
- 実際には、何が最も効果的ですか?