template .h/.cpp cannot be separate files??

Started by
26 comments, last by MaulingMonkey 18 years, 7 months ago
Good morning folks, I have a question about C++ templates. It's an area of C++ that I avoided for a long time, but having seen more and more examples of using them on the web, I've decided to buckle down and learn how to use them. There's a pretty good tutorial I found: http://www.cplusplus.com/doc/tutorial/tut5-1.html But one of the statements it makes is: "The macro-like functionality of templates, forces a restriction for multi-file projects: the implementation (definition) of a template class or function must be in the same file as the declaration. That means we cannot separate the interface in a separate header file and we must include both interface and implementation in any file that uses the templates. " Is this really true? I KNOW that I have seen code where the .h file and the.cpp file for a templated class are separated. Was this an older standard than is used currently in modern compilers? Thanks for your time! Mike
Advertisement
The template itself must be fully contained (interface and implementation) in the definition file. When you create a class from that template, that class can have its interface in a .h and its implementation in a .cpp.
One thing you can do is use what are known as "inline" files. These are header files that pretend to be source files. They have a lot of the same drawbacks as header files, but if you like to put your function bodies in a different file from your class definition, they'll work well for you.

Watch:

// MyClass.h#ifndef MYCLASS_H#define MYCLASS_Htemplate<typename T>class MyClass{public:    MyClass();};#include "MyClass.inl"#endif


// MyClass.inltemplate<typename T>MyClass<T>::MyClass(){    ....}
Definitions for templated functions and methods in templated classes must be visible to the compiler, ie. in the translation unit, of everywhere they are referenced. So it's best to just include the implementations in the header file where you declare them. Alternately, some people make a seperate .cpp file for the definitions, and include it where necessary, but this is, IMO, a very bad way to do things.

Now, the reason it has to be in the same translation block is that because of the way templates are handled, no code is created for them unless they are used. However, when you use them, code is generated on the fly for that specific template parameter/combination of parameters. So the function definitions need to be available to the compiler to make the code from. It took me a while to understand templates to start with, but once I got that gem of informations, it all became simple. And I realized why they were called templates [grin]

If that was not understandable, please tell me, and I'll try to explain better, because I fear that that explanation is rather badly written..
Free speech for the living, dead men tell no tales,Your laughing finger will never point again...Omerta!Sing for me now!
Ok, but check out the code at:

http://www.geometrictools.com/Intersection.html

All of the intersection classes defined here are templated, but the definitions and implementations are separated just like any normal class.

So are you supposed to #include the .cpp instead of the .h if you use a template laid out in this way?

Mike
There are a few technicalities.

A template must be contained in one translation unit, a limitation of compiler technology (and probably of the language specification). Header files do not comprise translation units; source files do (header files are not independently compiled). The problem with templates is that you typically provide the complete definition and then let the consumer code instantiate the template for an arbitrary type that conforms to the template requirements at compile-time. If you do not wish to do this, however, providing only a fixed number of type instantiations (and thus potentially lessening the value of your template to your consumer - imagine not being able to instantiate std::vector for your own types), then you can explicitly instantiate your template within the implementation unit. This is what the Geometric Tools' Intersections library does.

Look at it this way: with a "traditional" template, the entire template is in a header file and the compilation unit is comprised of consumer code, the template definition in the header and any other included headers. Compiler generates instantiations at compile time, everything's hunky-dory. In the Intersections library templates, the template declaration is in a header, its definition is in an implementation file, and explicit instantiations are at the bottom of said implementation file. You can not apply those templates to your own arbitrary types, or to types for which explicit instantiations were not built into the vendor-supplied library without modifying and recompiling the original vendor source.

So, it's not the same. If your template is designed to provide genericity for an open number of types, then it all has to be placed in the header. If it simply reduces redundant coding for a fixed set of type dependencies, then you can break it into header/implementation, so long as you explicitly instantiate in the implementation file.

Happy hacking.
Quote:Original post by Oluseyi
There are a few technicalities.

A template must be contained in one translation unit, a limitation of compiler technology (and probably of the language specification).

Not part of the language specification. "Seperate compilation" is possible through the export keyword as specified in the the C++ standard. Of course, that's not a widely implemented part of the standard. And technically a translation unit is more like a preprocessed source file, which incorporates the contents of the headers that it includes.
Quote:Original post by Michael Kron
So are you supposed to #include the .cpp instead of the .h if you use a template laid out in this way?


#including .cpp files is a definate no-no IMO - you'll end up with that file's data in two or more seperate compile units (one for the .cpp, one for the .cpp that #included the .cpp) leading to multiple definition errors.

Yet, a _lot_ of (arguibly stupid or misguided) people choose to lay out their templates just like that, so you're "supposed to" do just that.

I prefer the method used by my standard library - .hpp for C++ headers (these are #included by stub files like "iostream"), .cpp for C++ source files (I'm assuming), and .tcc (or .tpp or similar) for template sources (which can be #included by a header or a source file, depending on wheither you want the automatic-but-duplicating or manual-but-efficient options provided by most compilers).



If you're working with someone else's code, however, it may be simpler to just tolerate their wayword ways.
First of all.

HOLY FREAKING CRAP.


Second. I think I understand what you are saying Oluseyi. I was banging my head against the Geometric Tools code and I noticed those "explicit instantiations" for floats and doubles. Having never seen anything like that before, I was confused, but I imagined that it only allowed those types. Mind you, I have never seen syntax like this in my life, and I still don't really understand the flow of events that occurs when the user attempts to create an Intersector instance. Templates made sense when I thought of them as big macro wrappers around unknown types, but "explicit instantiation" boggles me.


Third. So if the template definition is not in the same file as the template declaration, then the compiler will ASSUME you intend to explicitly instantiate it, and compiles it on the spot? If you never had the explicit instantiations, would it just bark at you?


And lastly. If all they're doing is making code that performs similar operations on floats or doubles, why not use polymorphism? If they had structs for vectors, lines, etc. that inherit from a 'number' base class or interface( then subclassing 'NumberVec3' with 'FloatVec3', DoubleVec3 ), wouldn't this be a lot more intuitive and easy to read? It just seems like they're using a tool that is supposed to be a really generic 'catchall' for a couple cases. Maybe that's not the point, or maybe I'm just not used to templates enough.

By the way, thanks so much for all your help with this.
Mike
They're going for speed. Using base classes like that would be slower than using explicit template instantiations.

This topic is closed to new replies.

Advertisement