Where to place the member function template

The C ++ aspect that periodically disappoints me is a solution where templates are suitable for header files (traditionally describing the interface) and implementation files (.cpp). Templates often need to go in the header, exposing the implementation, and sometimes pulling out additional headers that previously only needed to be included in the .cpp file. I recently ran into this problem and a simplified example of this 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); } 

Please note that I intend to use my actual class as a base class, hence a virtual destructor; I doubt this is important, but I left it in Counter just in case. The result obtained above is equal to

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

Now the Counter declaration of the class declaration can go in the header file (for example, counter.h). I can put the dtor implementation, which requires iostream, in counter.cpp. But what to do for the countAndPrint() member function template, which also uses iostream? This does not need to be used in counter.cpp, since it must be created outside of the compiled counter.o. But including it in counter.h means that something, including counter.h, also in turn includes iostream, which just seems wrong (and I agree that I just need to overcome this disgust). I could also put the template code in a separate file (counter.t?), But that would be a bit surprising for other code users. Lakos doesn’t actually fit into this as we would like, and C ++ FAQs do not go into best practices. So what I need:

  • Are there any alternatives for dividing the code into the ones I suggested?
  • in practice, what works best?
+4
source share
3 answers

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

  • Private member templates must be defined in a .cpp file (unless they should be called by friends of your class template).
  • Infrequent member templates should be defined in the headers if they are not explicitly created.

Often you may not include many headers, making the names depend, thereby delaying the search and / or determining their meaning. Thus, you need a complete set of headers only at the time 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_; } }; 

This is what I used to implement a visitor template using CRTP. At first it looked like this:

 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 require the headers of all instruction classes due to these static throws. This way I made the types 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; } // ... } } }; 
+5
source

Google Style Guide suggests putting the template code in the file "counter-inl.h". If you want to be very careful in your inclusion, this may be the best way.

However, customers getting the iostream header with a "crash" are probably a small price to pay for all your class code in one logical place - at least if you only have one member function template.

+1
source

Almost your only options are to place the entire code template in the header or put the code in the .tcc file and include this file at the end of your header.

Also, if possible, you should try to avoid #include ing <iostream> in the headers, because it has a significant fee at compile time. After all, headers are often #include d multiple implementation files. Your header needs only code and inline code. The destructor does not have to be in the header.

+1
source

All Articles