You are here:

C++/Putting all templates in header file

Advertisement


Question
Dear expert,

I need to know if it is a good idea to put everything in the header file when working with templates?
Some say templates are a pure compile-time mechanism so it's better to be in the header file. I have also read on line that it may effect the compile time.
your prompt answer is much appreciated.

Answer
I would like to address your last request first.

Asking for a prompt reply is not going to get you a reply any faster as I answer questions as soon after I receive an email with a question as I can. (In fact it is likely to annoy me as there is an implication that I am tardy and therefore need to be prodded into action. Not a very polite thing to do when asking someone who is volunteering to help and is free to reject your request.)

I usually therefore reply to questions within 24 hours of the question being posted but it may be longer if I am away and do not pick up my email. I often do not get to my email before midday UK time [UTC (GMT) or BST] so posting questions during UK evening and night times will mean you are going to have to wait a bit for a reply until I pick up my email, sort out anything urgent and can formulate a reply - which may take ten minutes or may take several hours depending on the details of the requirements of the answer (your answer is in the latter category).

In any case you should always get a reply within 3 days of posting as this is a requirement of being an AllExperts volunteer expert (who is not on vacation <g>).

OK, rant over, on to your question.

The short answers are:
   - Yes, no, it depends (see below...)
   - Yes templates are a pure compile time mechanism in C++
     (compared to say .NET generics which require support from the runtime).
   - Yes they do effect the compile time.

As to where you put parts of a (shared) template definition, it depends on what method of template instantiation a compiler uses. It is made more confusing by the number of different but similar ways to group how templated are instantiated. I am going with terms that were used in an ACCU Overload magazine article that seems to best fit what we are concerned with here. The full article is online at http://accu.org/index.php/journals/361; the terms I will use are in the section headed "Template Instantiation Mechanisms". Even so it is still not clear if these terms do in fact cover the way things are in all cases as I describe here - it is best then to refer to compiler documentation to see what the requirements for laying out code for template definitions are.

Moving on, templates are odd beasts as they do not define classes or functions but templates that define (for the compiler) how to create classes or functions when supplied with sets of template parameters and therefore can be used to define whole families of classes or functions. These template definitions, along with a set of template parameters (together called a template specialisation), are used by the compiler to create a specific class or function at what is called a point of instantiation of a template. At such points the _full_ definition of the template must be available to the compiler [note: I shall ignore export for templates as it is only implemented by a few compilers].

The problems occur for parts of a template specialisation instantiation that have linkage - that is that are seen by the linker. These are non-inline function definitions and global static data. When combined with template specialisations these could occur as instantiations of: a non-inline function template, a class template with non-inline member functions, a class or class template with non-inline member function templates, or a class template with static data members.

The reason linkable entities resulting from template specialisation instantiations are a problem is that the point where we have all the information to instantiate such an entity is different from where we would usually place such a definition. Such definitions (for non-template code) are usually placed in a source code implementation file that is then compiled (i.e. a compilation unit), and compiled separately to the places where such entities are used [e.g. a function definition occurs in a single compilation unit (.cpp file) but may be used - called - in many compilation units, the only information the using compilation units require is a function declaration, e.g. placed in a header file they all include].

However this does not work with the equivalent template definitions because such a template definition is _not_ a definition of any specific function or static data item _until_ it is specialised and instantiated - requiring a set of template parameters - which is done at the point(s) of instantiation - probably in other compilation units.

To put it another way: in order to create an actual entity from a template definition we require three parts: template definition (TD) and a template specialisation instantiation (TSI) with template arguments (TA). We could represent this like so:

   TD + TSI[TA] = function or class or class part entity.
   
This is not a problem for non-linkable entities because in these cases TD and TSI[TA] are in the same compilation unit.

The problem with the linkable entities is that if the TD part is separate from the TSI[TA] part(s), as per non-template definition practice then how do we know which TSI[TA]s to apply to each TD to generate the required entities for a program or library? On the other hand if we keep the TD and TSI[TA} parts in the same compilation units then, because these are linkable entities, how do we cope with duplicate identical entity definitions that will occur if the same TSI[TA] for a TD occurs in multiple compilation units as this would under normal circumstances break the one definition rule (ODR) and cause the linker to emit errors?

This of course gives rise to two possible solution schemes:

   -   'local' schemes where all the instantiations of a template are done locally for each compilation unit.
       TD and TSI[TA] are always in the same compilation unit and the problems with duplicate definitions is sorted out between the compiler and the linker (or some other scheme is used).

   -   'global' schemes where template instantiations are done when considering all compilation units in a program or library.
       For linkable entities TD and TSI[TA] are separate and the problem of which set of TAs to apply to each TD is solved at this global program level.

Local template instantiation schemes are the most common and require all the code for a template definition be available to each compilation unit where it is required [i.e. where an instantiation of (a specialisation of) that template occurs]. This is the case referred to in your question when you say that its a "good idea to put everything in a header file when working with templates". [In fact your statement is not strictly true as anything that is not part of a template definition would be placed in either a header file (.h, .hpp, hxx etc.) or an implementation (.cpp, .cxx,etc.) as usual. Also if the template definition is not shared but internal to a single implementation file it could of course be placed into the single implementation file where it is used.]. Local schemes are generally used by Microsoft Windows development environments (although not for all compilers that have MS Windows editions).

For global template instantiation schemes only the usual candidates for inclusion in header files need to be placed into a header file for a template definition. Any definitions for linkable entities are placed in an implementation file that is intended to be directly compiled.

So to sum up:

   - There are two main template instantiation schemes around
       - local schemes that require us to "put everything in the header
         file when working with templates" and are the most common.

       - global schemes that require us to split template definitions
         into a header part and implementation part as per non-template
         class/function definitions/declarations.

   - Check C++ compiler documentation to see what template instantiation models
     are supported and what any file naming and placement requirements are.

If you have to write template definitions that may be built using either the global or local schemes (usually because such code is meant to be compiled using multiple compilers) then split the code as for the global scheme but use pre-processor macros and conditional compilation directives to ensure each part includes the other as required for each case. For example say we have the following class template:

   template <typename T>
   class CT
   {
       T       v_;

   public:
       CT( T const & v ) : v_(v) {}
       
       T DoSomeOperation();
   };

   // Non-inline template class member function definition.
   template <typename T>
   T  CT<T>::DoSomeOperation()
   {
       T result;

   // Do calculations...

       return result;
   }

In the local instantiation scheme case we would ensure each compilation unit that needed to instantiate a CT specialisation has access to _all_ parts of the CT definition by placing all the above in a single header like so:

   //--------------------------------------------------------------------------
   // ct.hpp
   
   #ifndef CT_HPP
   #define CT_HPP

   template <typename T>
   class CT
   {
       T       v_;

   public:
       CT( T const & v ) : v_(v) {}
       
       T DoSomeOperation();
   };

   // Non-inline template class member function.
   template <typename T>
   T  CT<T>::DoSomeOperation()
   {
       T result;

   // Do calculations...

       return result;
   }

   #endif    
   //--------------------------------------------------------------------------

For the global instantiation scheme, those definition parts that result in linkable entities are used to generate instantiations of those parts of a CT specialisation globally for the whole program and separate from the compilation units where they are used. So in this case we need to split the definition into two parts: a header file and a compilable implementation file:

   //--------------------------------------------------------------------------
   // ct.hpp
   
   #ifndef CT_HPP
   #define CT_HPP

   template <typename T>
   class CT
   {
       T       v_;

   public:
       CT( T const & v ) : v_(v) {}
       
       T DoSomeOperation();
   };
   #endif    
   //--------------------------------------------------------------------------

   //--------------------------------------------------------------------------
   // ct.cpp
   
   #include "ct.hpp"

   // Non-inline template class member function.
   template <typename T>
   T  CT<T>::DoSomeOperation()
   {
       T result;

   // Do calculations...

       return result;
   }
   //--------------------------------------------------------------------------

Now what if we need to support both models?

Basically we keep the layout used for the global instantiation scheme _but_ conditionally include the cpp file in the hpp file:

   //--------------------------------------------------------------------------
   // ct.hpp
   
   #ifndef CT_HPP
   # define CT_HPP

   template <typename T>
   class CT
   {
       T       v_;

   public:
       CT( T const & v ) : v_(v) {}
       
       T DoSomeOperation();
   };
   
   # ifdef USING_LOCAL_TEMPLATE_INSTANTIATION
   #  include "ct.cpp"
   # endif
   #endif    
   //--------------------------------------------------------------------------

   //--------------------------------------------------------------------------
   // ct.cpp
   
   #include "ct.hpp"

   // Non-inline template class member function.
   template <typename T>
   T  CT<T>::DoSomeOperation()
   {
       T result;

   // Do calculations...

       return result;
   }
   //--------------------------------------------------------------------------

This works as follows:

When using a local instantiation scheme we define the C++ pre-processor macro USING_LOCAL_TEMPLATE_INSTANTIATION - this could be done on the command line or we could place it in a configuration header file which all source files include (which is not shown in my examples).

All code that requires the CT class template includes ct.hpp.

If USING_LOCAL_TEMPLATE_INSTANTIATION is defined then this will cause ct.cpp to be included to bring in at this point the linkable parts of the definition. Of course ct.cpp will include ct.hpp, but in this case finds that CT_HPP is defined so skips the body of the header file. We could if we wanted prevent this by using the inverse check for USING_LOCAL_TEMPLATE_INSTANTIATION in ct.cpp around the inclusion of ct.hpp.

If on the other hand we are using a global instantiation scheme then USING_LOCAL_TEMPLATE_INSTANTIATION is not defined so any code including ct.hpp will not cause ct.cpp to be included and ct.cpp will be used to generate entities as some other - presumably later - point in the program build cycle (e.g. during a pre-linking stage).

As local instantiation schemes are the more common case it might be worth while inverting the sense of this inclusion logic, for example by defining a macro USING_GLOBAL_TEMPLATE_INSTANTIATION and only including ct.cpp from ct.hpp if it is _not_ defined. Then something only needs to be defined for those compilers which do not use a local instantiation scheme.

For much, much, greater detail on C++ templates I recommend you obtain (and read!) a copy of the book "C++ Templates, The Complete Guide" by David Vandevoorde and Nicolai M. Josuttis, published by Addison-Wesley in 2003.

On a final note: the code snippets shown here are for example purposes only and I have not compiled them so may contain typos or other errors if so then I apologise.

Hope this helps.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.