21

I'm wondering a bit about this templating business.

In C and C++ it is very common to put declarations in header files and definitions in source files, and keep the two completely separate. However, this doesn't even seem to be possible (in any great way) when it comes to templates, and as we all know, templates are a great tool.

Also, Boost is mostly headers, so this is a real issue. Is separating headers and source still a good idea in C++, or should I just not rely heavily on templates?

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
Max
  • 4,345
  • 8
  • 38
  • 64

5 Answers5

19

Instantiating a template is costly at compile time but virtualy free at runtime. Basically, everytime you use a new template type, the compiler has to generate the code for that new type, that's why the code is in a header, so that the compiler have access to the code later.

Putting all your code in a .cpp lets the compiler compile that code only once which greatly speeds up compilation. You could in theory write all your code in headers, it will work fine, but it will take forever to compile very large projects. Also, as soon as you will change one line anywhere, you will have to rebuild everything.

Now you might ask, how comes the STL and BOOST are not so slow? That's where precompiled headers come to the rescue. PCHs let the compiler do the most costly work only once. This works well with code that won't change often like libraries, but its effect is totally nullified for code that changes a lot as you will have to recompile the whole set of precompiled headers everytime. The compiler also use a couple of tricks to avoid recompiling all template code in every compilation unit.

Also note that C++0x will introduce explicit mechanisms to better control template instantiation. You will be able to explicitly instantiate templates and, most importantly, prevent instanciation in some compilation units. However, most of that work is already being done by most compilers without our knowledge.

So, the rule of thumb is, put as much code (and include directives) as possible in your .cpp. If you can't, well, you can't.

My advice would be: don't template just for the heck of it. If you have to template, be careful and be aware that you are in fact choosing between speed of compilation and usability the template will bring.

Coincoin
  • 27,880
  • 7
  • 55
  • 76
7

My personal favourite is this structure:

Header file:

#ifndef HEADER_FILE
#define HEADER_FILE

template < typename T > 
class X { void method(); };

#include "header_impl.h"

#endif

Implementation file:

#ifndef HEADER_IMPL_FILE
#define HEADER_IMPL_FILE

#include "header.h"

template < typename T > 
void X<T>::method() { }

#endif
Šimon Tóth
  • 35,456
  • 20
  • 106
  • 151
  • 3
    -1 whilst it may be, for some reason, your favourite, that information is not very useful without saying what its advantages are, and how it gets around the cases where T isn't known until the template is used. (having if-guards on the impl implies you are including the impl somewhere, though you don't say so, and having two included files would be much the same as having a larger header file) – Pete Kirkham Feb 09 '11 at 20:47
  • @Pete Uh? T is never known until the template is used, that's one of the major points of templates. Sure you can use templates even for non-parametric code, but that defeats the purpose. I forgot one line in the code (it's almost exclusively for coding tools), the reason for the guards should be clear now. – Šimon Tóth Feb 09 '11 at 21:08
  • +1, that's what I usually do too, but without including "header.h" in the "_impl.h" again. Maybe useful for IntelliSense, but not necessary. – Xeo Feb 09 '11 at 21:21
  • 1
    You shouldn't probably include "header.h" in the "implementation file", and I don't think it's properly an implementation file in the sense of a regular cpp file. This is just a way to split a large file into two and helps move clutter out of the header file. – UncleBens Feb 09 '11 at 21:21
  • 1
    @Uncle The include is for the purpose of not confusing coding tools (editors, static analyzers, etc...). And no, it's not an implementation file since you can't put templates into implementation files (with the exception of a few corner cases, that you will probably never encounter). – Šimon Tóth Feb 10 '11 at 10:21
  • I think it's also common to give the included file a different extension, such as "tcc" (as in G++'s stdlib). Wouldn't that help to unconfuse tools? It's not a real header, it's just a piece of text to be inserted into a header. – UncleBens Feb 10 '11 at 16:01
2

I think the really important thing to understand about templates is that, paraphrasing Bjarne Stroustrop, C++ is really like multiple languages rolled into one. The conventions and idioms of templating are different than those of writing "regular" C++, almost like another language.

It is absolutely a good idea to separate header and implementation files in "regular" C++, because the header files tell the compiler what you will supply as an implementation some time later (at link time). It is important because this separation is a very real thing in most common operating systems: link time can happen when the user runs the program. You can compile the implementation into binaries (so's, dll's) and ship the unaltered headers for developers to know how to use your now-opaque implementation.

Now, for templates, you can't do that because the compiler has to fully resolve everything at compile time. That is, when you compile the headers, they have to have the implementation of the template in order for the compiler to be able to make sense of your headers. The compiler essentially "un-templates" your templates when you compile, so there is no option for separating interface and implementation of template C++.

Separation of interface and implementation is good practice, generally, but when you wander into template-land, you are explicitly going to a place where that's just not possible - because that's what a template means: resolve and build the implementation of this interface at compile time, not at runtime.

Matt
  • 10,434
  • 1
  • 36
  • 45
1

Here are a couple of techniques I use when writing templated code that move implementations into cpp files:

  1. Move parts of methods that do not depend on the template parameters into separate, non-template helper functions or base classes. The bodies can then go into their own cpp files.

  2. Write declarations of common specializations. The definitions can live in their own cpp files.

quamrana
  • 37,849
  • 12
  • 53
  • 71
-1

Separating header and source is not a C++ idiom, it is more of a C idiom, precisely because C++ favours use of templates and inline functions where possible to reduce the runtime of the program.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • You can use a whole program optimizer for that. If you put too many things in header files it slows down compiles, especially in non-trivial projects. – Neil Kirk Aug 21 '15 at 03:32