4

For some reason, it seems that C++ doesn't like it when you split a template class (i.e. a class declared as template <typename T> class Thing) into a .h and a .cpp file, like you would do with any other class.

Does that mean that when writing a template class, I should just write it all in the header file? What do C++ programmers do in these cases?

EDIT: I understand that there are alternatives to writing it all in the .h file. But what's the best option? Or what the most common option?

Aviv Cohn
  • 15,543
  • 25
  • 68
  • 131
  • C++ doesn't mind it, you just have to help the compiler by explicitly instantiating the template types in the file the body of the function can be found. – Jonathan Potter Oct 04 '14 at 22:07
  • 1
    http://stackoverflow.com/q/495021/440558 – Some programmer dude Oct 04 '14 at 22:07
  • @JoachimPileborg I just retained, to mark that as a dupe directly. I agree, it's deeply related though. – πάντα ῥεῖ Oct 04 '14 at 22:18
  • OK, are 4 enough to mark a dupe? Vote to reopen if anyone disagrees. – πάντα ῥεῖ Oct 05 '14 at 00:51
  • Write the implementation in .inl file and #include it at the end of the .h file. That way you don't need to have messy header files that hurt readibility. In some cases if a template is used only in one cpp, you can have the template implementation in the cpp, but this is more rare (e.g. nested private template class or specific available instantiations explicitly instantiated in the cpp) – JarkkoL Oct 05 '14 at 02:16

2 Answers2

5

It is popular to split the definition of a template out into another file. It is so popular it may even be the most common practice, but I am not positive about that.

I think this is poor practice (as opposed to putting the definitions in the same header) for the following reasons.

My priorities when writing code are (most important first):

  1. Correctness.
  2. Run time performance.
  3. Compile time performance.
  4. Maintainability / Easy to read.
  5. Easy to write.

As long as the template definition is in the same header, or in another file that the header includes, the number one priority Correctness is not impacted one way or the other.

The second priority Run time performance is not impacted in the least.

The third priority Compile time performance is negatively impacted by putting the definition in a separate file. By how much is debatable. But there is no way that a compiler can compile X amount of code and open/close two files, as fast as it can compile the exact same code while opening/closing only one file. A friend of mine who is an excellent compiler writer once told me that the slowest thing I could ask his compiler to do was to open a file. This was before we did as much compile-time computation as we do today, but still...

The fourth priority Maintainability / Easy to read is quite subjective. But in my opinion a separate file is a negative impact on this priority. When I'm reading code, it is typically easier for me to understand it if it is all in one file. And I really get annoyed when I have to go hunting for the file when its name or location is not obvious. And I get even more annoyed when the definition is split into many files. For my money, one file optimizes this priority.

The fifth priority Easy to write is also quite subjective. I don't see much advantage either way on this one. It is very slightly easier to put everything in one file. But it is certainly not difficult to create a new file. I give the "one file approach" a very slight advantage on this priority.

So in summary, out of my five priorities, the two most important, this decision makes absolutely no difference on, and separate files have small negative consequences for the 3 lower priorities. Of the 3 lower priorities, the most important of these, compile-time performance is objectively negatively impacted. The two lowest priorities are admittedly subjective as to whether or not they are positively or negatively impacted.

I see no benefit and a small cost to putting your template definitions in a separate header.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    _" I see no benefit and a small cost to putting your template definitions in a separate header."_ Absolutely depends on complexity (and thus readability) of the header files though IMHO. Also there could be cases you wan't to control the actually chosen implementation by `#if defined(XY)` directives. Could be, we should close the question as being too broad at least, or go for the dupe (that has some points about the consequences of this principle as well). – πάντα ῥεῖ Oct 04 '14 at 23:53
  • I don't think this is a dupe of http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file . It is clear to me that the answer to this question is quite subjective. But for someone not familiar with C++, I don't see that the question is obviously subjective. I think this is a decent question (not worthy of closing). I've upvoted the question. – Howard Hinnant Oct 05 '14 at 00:00
  • Well I decided to mark it as a dupe finally. IMHO, what's _the best way_ to handle this is opinion/situational context based anyway. – πάντα ῥεῖ Oct 05 '14 at 00:59
  • `The second priority Run time performance is not impacted in the least.` Is this even true? I would guess that function templates provided in a header can be inlined by the compiler when the definitions is included into a cpp file, but compiling instantiations of specific function templates and linking later would not allow for inlining. Maybe someone more knowledgeable than me can comment on this. – jmracek Nov 08 '20 at 20:57
  • It sounds like you are referring to explicit template instantiations (http://eel.is/c++draft/temp.explicit). I.e. those introduced with the `extern` keyword. Such objects can not have internal linkage (http://eel.is/c++draft/temp.explicit#14), and thus absolutely belong in a source file. If you were to put them in a header you would likely get a "doubly-defined" error message from your linker. – Howard Hinnant Nov 09 '20 at 14:04
3

"Does that mean that when writing a template class, I should just write it all in the header file? What do C++ programmers do in these cases?"

What you can do (and is wide spread/popular practice), is separate implementation code out to special template implementation files, that will in turn be in íncluded by the template headers, containing the declarations.
The gain of this technique is considered little for most of the cases, though it has it's points, if you want to hide the implementation details and not spill header files to become large.

The point is not to have the template definition code, or specializations in separate translation units, such these can be seen directly by other translation units, including the template header file.

The common pattern is

MyTemplate.h

#if !defined(MYTEMPLATE_H_)
#define MYTEMPLATE_H_
namespace myspace {
    template <typename T> 
    class MyTemplate {
    public:
        void foo(T value);
    };

#include "MyTemplate.tcc"
}
#endif // MYTEMPLATE_H_

MyTemplate.tcc

// NOTE:  There aren't header guards for this file intentionally!
template<typename T>
void MyTemplate<T>::foo(T value) {
   // ...
}

Other popular extensions for template implementation files are .icc, .ipp, .impl. Just important, it shouldn't be .cpp, since most IDE's or build system frameworks will track this as translation unit, unless it's explicitly excluded (here's a sample why).


"So simply instead of the .cpp #includeing the header, the header #includes the .tpp (which contains the implementations)?"

Template classes work a bit differently regarding the ODR (one definition rule). Regular header files, that provide class declarations shouldn't contain implementations because the ODR would be violated when these are included from different translation units:

MyClass.h

class MyClass {
public:
    void foo(int);
};

// Violates ODR if MyClass.h is included from different translation units (.cpp files)
void MyClass::foo(int param) {
}

The templated version

template<typename T>
class MyClass {
public:
    void foo(T);
};

// Doesn't violate ODR if MyClass.h is included from different translation units 
// (.cpp files), since the template parameter isn't instatiated here yet.
template<typename T>
void MyClass<T>::foo(T param) {
}

As soon one translation unit instantiated something like MyClass<int>, other translation units instatiating the same template signature, will use the 1st one seen.

The latter implementation part could be replaced with an #include "MyClass.impl" that contains that code, if you think it messes up readability or maintainability of your header file too much.

As a minor drawback of the #include "MyClass.tcc" technique, you should notice, that most of the popular IDE's handle syntax highlighting and intellisense poorly for these kind of template implementation files.

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • So simply instead of the .cpp #including the header, the header #includes the .tpp (which contains the implementations)? – Aviv Cohn Oct 04 '14 at 22:30
  • 1
    @AvivCohn _"So simply instead of the .cpp #including the header"_ That has slightly different meanings. Wait a minute, I'll update my question. – πάντα ῥεῖ Oct 04 '14 at 22:33
  • I hate this approach. Just put it in one file. – Neil Kirk Oct 04 '14 at 22:41
  • @NeilKirk Well, it has it's pros and cons. You can't deny it's _common practice_ though (including the standard library implementations). – πάντα ῥεῖ Oct 04 '14 at 22:46
  • @NeilKirk Readability (hiding details), and possible selective implementations based on `#if defined(XY)` controlled sections, instead of using template policies (which might be more complicated). – πάντα ῥεῖ Oct 05 '14 at 00:03
  • Putting it into a different file makes it harder to read. I've tried to debug such files and found VS intelliense doesn't understand these impl files. Also why can't you use #ifs in the same file? You can still put the functions on the bottom of the file. – Neil Kirk Oct 05 '14 at 00:06
  • 1
    Just a note: I've seen `_impl.h` (or `_impl.hpp`) used as a suffix for this purpose. It's pretty obvious, and you don't have to confuse the poor text editors' syntax highlighting by creating new extensions. – Mike DeSimone Oct 05 '14 at 00:15
  • @NeilKirk I mentioned both opportunities though, and what's the reasoning (and drawbacks) behind them. Could we aggree about this at least? – πάντα ῥεῖ Oct 05 '14 at 00:15
  • @MikeDeSimone Good point. I didn't even consider so yet (me stupid). It would well solve all of the concerns brought up with the sample question I've linked. – πάντα ῥεῖ Oct 05 '14 at 00:18
  • 2
    Instead of *“NOTE: There aren't header guards for this file intentionally!”* I have also seen `#ifndef INCLUDED_FROM_MYHEADER`, `#error "Never '#include ' directly, '#include ' instead"`, `#endif`. With `myheader.h` containing `#define INCLUDED_FROM_MYHADER`, `#include `, `#undef INCLUDED_FROM_MYHADER`. I kind of like this extra guard. – 5gon12eder Oct 05 '14 at 00:46