gpt4 book ai didi

c++ - 成员函数模板放在哪里

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:32:27 24 4
gpt4 key购买 nike

定期让我感到沮丧的 C++ 方面是决定模板在头文件(传统上描述接口(interface))和实现 (.cpp) 文件之间的位置。模板通常需要放在 header 中,公开实现,有时还需要引入额外的 header ,而这些 header 以前只需要包含在 .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 中。但是同样使用iostream的成员函数模板countAndPrint()怎么办呢?它在 counter.cpp 中没有用,因为它需要在已编译的 counter.o 之外实例化。但是把它放在 counter.h 中意味着包括 counter.h 在内的任何东西也反过来包括 iostream,这似乎是错误的(我承认我可能只需要克服这种厌恶)。我也可以将模板代码放入一个单独的文件(counter.t?),但这对代码的其他用户来说有点奇怪。 Lakos并没有真正像我想的那样深入,而且 C++ FAQ没有进入最佳实践。所以我追求的是:

  1. 是否有任何替代方法可以将代码划分为我建议的代码?
  2. 在实践中,什么最有效?

最佳答案

经验法则(原因应该很清楚)。

  • 私有(private)成员模板应该在 .cpp 文件中定义(除非它们需要被您的类模板的 friend 调用)。
  • 非私有(private)成员模板应在 header 中定义,除非它们已明确实例化。

您通常可以通过使名称相互依赖来避免包含大量 header ,从而延迟查找和/或确定它们的含义。这样,您仅在实例化时需要完整的 header 集。举个例子

#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;
}
// ...
}
}
};

由于那些静态转换,这将需要所有语句类的 header 。所以我让类型是依赖的,然后我只需要前向声明

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;
}
// ...
}
}
};

关于c++ - 成员函数模板放在哪里,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4111932/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com