经验法则(其原因应该很清楚)。
- 私有成员模板应在 .cpp 文件中定义(除非它们需要由类模板的朋友调用)。
- 非私有成员模板应在标头中定义,除非它们被显式实例化。
您通常可以通过使名称具有相关性来避免包含大量标头,从而延迟查找和/或确定其含义。这样,您仅在实例化时才需要完整的标头集。举个例子
#include <iosfwd> // suffices
class Counter
{
unsigned int count_;
public:
Counter() : count_(0) {}
virtual ~Counter();
// in the .cpp file, this returns std::cout
std::ostream &getcout();
// makes a type artificially dependent
template<typename T, typename> struct ignore { typedef T type; };
template<class T>
void countAndPrint(const T&a) {
typename ignore<std::ostream, T>::type &cout = getcout();
cout << count_;
}
};
这就是我用来实现使用 CRTP 的访问者模式的方法。最初看起来像这样
template<typename Derived>
struct Visitor {
Derived *getd() { return static_cast<Derived*>(this); }
void visit(Stmt *s) {
switch(s->getKind()) {
case IfStmtKind: {
getd()->visitStmt(static_cast<IfStmt*>(s));
break;
}
case WhileStmtKind: {
getd()->visitStmt(static_cast<WhileStmt*>(s));
break;
}
// ...
}
}
};
由于这些静态转换,这将需要所有语句类的标头。所以我已经使类型成为依赖的,然后我只需要前向声明
template<typename T, typename> struct ignore { typedef T type; };
template<typename Derived>
struct Visitor {
Derived *getd() { return static_cast<Derived*>(this); }
void visit(Stmt *s) {
typename ignore<Stmt, Derived>::type *sd = s;
switch(s->getKind()) {
case IfStmtKind: {
getd()->visitStmt(static_cast<IfStmt*>(sd));
break;
}
case WhileStmtKind: {
getd()->visitStmt(static_cast<WhileStmt*>(sd));
break;
}
// ...
}
}
};