在哪里放置成员函数模板 [英] Where to put a member function template

查看:136
本文介绍了在哪里放置成员函数模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C ++的一个方面周期性地挫败我决定模板在头文件(传统上描述的接口)和实现(.cpp)文件之间的适合。模板通常需要进入标题,暴露实现,有时会拉出额外的标题,以前只需要包括在.cpp文件中。我最近再次遇到这个问题,它的一个简单的例子如下所示。

An aspect of C++ that periodically frustrates me is deciding where templates fit between header files (traditionally describing the interface) and implemention (.cpp) files. Templates often need to go in the header, exposing the implementation and sometimes pulling in extra headers which previously only needed to be included in the .cpp file. I encountered this problem yet again recently, and a simplified example of it is shown below.

#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);
}

请注意,我打算使用我的实际类作为基类,因此虚拟析构函数;我怀疑是重要的,但我把它留在柜台,以防万一。从上面得到的输出是

Note that I intend to use my actual class as a base class, hence the virtual destructor; I doubt it matters, but I've left it in Counter just in case. The resulting output from the above is

counted: 5
counted: 3.2
counted: (2,4)
total count=3

现在计数器的类声明都可以在头文件中(例如,counter.h)。我可以把dtor的实现,这需要iostream,进入counter.cpp。但是要做成员函数模板 countAndPrint(),它还使用iostream?它在counter.cpp中没有用,因为它需要在编译的counter.o之外被实例化。但是把它放在counter.h意味着任何东西,包括counter.h也包括iostream,这似乎是错误的(我承认,我可能只是要克服这种厌恶)。我也可以把模板代码放到一个单独的文件(counter.t?),但这将是一个令人惊讶的其他用户的代码。 Lakos 实际上并不像我想要的那么多,而且< a href =http://www.parashift.com/c++-faq-lite/templates.html#faq-35.13 =nofollow> C ++常见问题没有达到最佳做法。所以我的后面是:

Now Counter's class declaration could all go in a header file (e.g., counter.h). I can put the implementation of the dtor, which requires iostream, into counter.cpp. But what to do for the member function template countAndPrint(), which also uses iostream? It's no use in counter.cpp since it needs to be instantiated outside of the compiled counter.o. But putting it in counter.h means that anything including counter.h also in turn includes iostream, which just seems wrong (and I accept that I may just have to get over this aversion). I could also put the template code into a separate file (counter.t?), but that would be a bit surprising to other users of the code. Lakos doesn't really go into this as much as I'd like, and the C++ FAQ doesn't go into best practice. So what I'm after is:


  1. 有没有任何替代方法,将代码分成我建议的?

  2. 在实践中,效果最好?


推荐答案

(其原因应该清楚)。

A rule of thumb (the reason of which should be clear).


  • 私人会员模板应在.cpp文件中定义(除非他们需要你的类模板的朋友可以调用)。

  • 非私人成员模板应在标题中定义,除非它们被显式实例化。

通常可以避免必须通过使名称依赖而包括许多标题,从而延迟查找和/或确定其含义。这样,您只需在实例化点处需要完整的标头集。作为示例

You can often avoid having to include lots of headers by making names be dependent, thus delaying lookup and/or determination of their meaning. This way, you need the complete set of headers only at the point of instantiation. As an example

#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的访问者模式。最初看起来像这样

This is what I used for implementing a visitor pattern that uses CRTP. It looked like this initially

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

这将需要所有语句类的头,因为这些静态转型。所以我已经使类型是依赖的,然后我只需要前向声明

This will need the headers of all statement classes because of those static casts. So I have made the types be dependent, and then I only need forward declarations

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

这篇关于在哪里放置成员函数模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆