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
NowCounter
的类声明都可以放在头文件中(例如,counter.h)。我可以把需要iostream的dtor的实现放到counter.cpp中。但是对于countAndPrint()
同样使用 iostream 的成员函数 template 怎么办?它在 counter.cpp 中没有用,因为它需要在已编译的 counter.o 之外进行实例化。但是将它放在 counter.h 中意味着包括 counter.h 在内的任何内容也反过来包含 iostream,这似乎是错误的(我接受我可能只需要克服这种厌恶)。我也可以将模板代码放到一个单独的文件中(counter.t?),但这对于代码的其他用户来说有点令人惊讶。Lakos并没有像我想要的那样深入探讨这个问题, C++ 常见问题解答没有进入最佳实践。所以我追求的是:
- 是否有任何替代方法可以将代码划分为我建议的代码?
- 在实践中,什么最有效?